Purge Rejected Follow requests in daily task (#334)
[akkoma] / test / pleroma / web / twitter_api / remote_follow_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
6 use Pleroma.Web.ConnCase, async: false
7
8 alias Pleroma.MFA
9 alias Pleroma.MFA.TOTP
10 alias Pleroma.User
11 alias Pleroma.Web.CommonAPI
12
13 import ExUnit.CaptureLog
14 import Pleroma.Factory
15 import Ecto.Query
16
17 setup_all do: clear_config([:instance, :federating], true)
18 setup do: clear_config([:user, :deny_follow_blocked])
19
20 describe "GET /ostatus_subscribe - remote_follow/2" do
21 test "adds status to pleroma instance if the `acct` is a status", %{conn: conn} do
22 Tesla.Mock.mock(fn
23 %{method: :get, url: "https://mastodon.social/users/emelie/statuses/101849165031453009"} ->
24 %Tesla.Env{
25 status: 200,
26 headers: [{"content-type", "application/activity+json"}],
27 body: File.read!("test/fixtures/tesla_mock/status.emelie.json")
28 }
29
30 %{method: :get, url: "https://mastodon.social/users/emelie/collections/featured"} ->
31 %Tesla.Env{
32 status: 200,
33 headers: [{"content-type", "application/activity+json"}],
34 body:
35 File.read!("test/fixtures/users_mock/masto_featured.json")
36 |> String.replace("{{domain}}", "mastodon.social")
37 |> String.replace("{{nickname}}", "emelie")
38 }
39
40 %{method: :get, url: "https://mastodon.social/users/emelie"} ->
41 %Tesla.Env{
42 status: 200,
43 headers: [{"content-type", "application/activity+json"}],
44 body: File.read!("test/fixtures/tesla_mock/emelie.json")
45 }
46 end)
47
48 assert conn
49 |> get(
50 remote_follow_path(conn, :follow, %{
51 acct: "https://mastodon.social/users/emelie/statuses/101849165031453009"
52 })
53 )
54 |> redirected_to() =~ "/notice/"
55 end
56
57 test "show follow account page if the `acct` is a account link", %{conn: conn} do
58 Tesla.Mock.mock(fn
59 %{method: :get, url: "https://mastodon.social/users/emelie"} ->
60 %Tesla.Env{
61 status: 200,
62 headers: [{"content-type", "application/activity+json"}],
63 body: File.read!("test/fixtures/tesla_mock/emelie.json")
64 }
65
66 %{method: :get, url: "https://mastodon.social/users/emelie/collections/featured"} ->
67 %Tesla.Env{
68 status: 200,
69 headers: [{"content-type", "application/activity+json"}],
70 body:
71 File.read!("test/fixtures/users_mock/masto_featured.json")
72 |> String.replace("{{domain}}", "mastodon.social")
73 |> String.replace("{{nickname}}", "emelie")
74 }
75 end)
76
77 response =
78 conn
79 |> get(remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"}))
80 |> html_response(200)
81
82 assert response =~ "Log in to follow"
83 end
84
85 test "show follow page if the `acct` is a account link", %{conn: conn} do
86 Tesla.Mock.mock(fn
87 %{method: :get, url: "https://mastodon.social/users/emelie"} ->
88 %Tesla.Env{
89 status: 200,
90 headers: [{"content-type", "application/activity+json"}],
91 body: File.read!("test/fixtures/tesla_mock/emelie.json")
92 }
93
94 %{method: :get, url: "https://mastodon.social/users/emelie/collections/featured"} ->
95 %Tesla.Env{
96 status: 200,
97 headers: [{"content-type", "application/activity+json"}],
98 body:
99 File.read!("test/fixtures/users_mock/masto_featured.json")
100 |> String.replace("{{domain}}", "mastodon.social")
101 |> String.replace("{{nickname}}", "emelie")
102 }
103 end)
104
105 user = insert(:user)
106
107 response =
108 conn
109 |> assign(:user, user)
110 |> get(remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"}))
111 |> html_response(200)
112
113 assert response =~ "Remote follow"
114 end
115
116 test "show follow page with error when user can not be fetched by `acct` link", %{conn: conn} do
117 Tesla.Mock.mock(fn
118 %{method: :get, url: "https://mastodon.social/users/not_found"} ->
119 %Tesla.Env{
120 status: 404
121 }
122 end)
123
124 user = insert(:user)
125
126 assert capture_log(fn ->
127 response =
128 conn
129 |> assign(:user, user)
130 |> get(
131 remote_follow_path(conn, :follow, %{
132 acct: "https://mastodon.social/users/not_found"
133 })
134 )
135 |> html_response(200)
136
137 assert response =~ "Error fetching user"
138 end) =~ "Object has been deleted"
139 end
140 end
141
142 describe "POST /ostatus_subscribe - do_follow/2 with assigned user " do
143 test "required `follow | write:follows` scope", %{conn: conn} do
144 user = insert(:user)
145 user2 = insert(:user)
146 read_token = insert(:oauth_token, user: user, scopes: ["read"])
147
148 assert capture_log(fn ->
149 response =
150 conn
151 |> assign(:user, user)
152 |> assign(:token, read_token)
153 |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
154 |> response(200)
155
156 assert response =~ "Error following account"
157 end) =~ "Insufficient permissions: follow | write:follows."
158 end
159
160 test "follows user", %{conn: conn} do
161 user = insert(:user)
162 user2 = insert(:user)
163
164 conn =
165 conn
166 |> assign(:user, user)
167 |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"]))
168 |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
169
170 assert redirected_to(conn) == "/users/#{user2.id}"
171 end
172
173 test "returns error when user is deactivated", %{conn: conn} do
174 user = insert(:user, is_active: false)
175 user2 = insert(:user)
176
177 response =
178 conn
179 |> assign(:user, user)
180 |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
181 |> response(200)
182
183 assert response =~ "Error following account"
184 end
185
186 test "returns error when user is blocked", %{conn: conn} do
187 clear_config([:user, :deny_follow_blocked], true)
188 user = insert(:user)
189 user2 = insert(:user)
190
191 {:ok, _user_block} = Pleroma.User.block(user2, user)
192
193 response =
194 conn
195 |> assign(:user, user)
196 |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
197 |> response(200)
198
199 assert response =~ "Error following account"
200 end
201
202 test "returns error when followee not found", %{conn: conn} do
203 user = insert(:user)
204
205 response =
206 conn
207 |> assign(:user, user)
208 |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => "jimm"}})
209 |> response(200)
210
211 assert response =~ "Error following account"
212 end
213
214 test "returns success result when user already in followers", %{conn: conn} do
215 user = insert(:user)
216 user2 = insert(:user)
217 {:ok, _, _, _} = CommonAPI.follow(user, user2)
218
219 conn =
220 conn
221 |> assign(:user, refresh_record(user))
222 |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"]))
223 |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
224
225 assert redirected_to(conn) == "/users/#{user2.id}"
226 end
227 end
228
229 describe "POST /ostatus_subscribe - follow/2 with enabled Two-Factor Auth " do
230 test "render the MFA login form", %{conn: conn} do
231 otp_secret = TOTP.generate_secret()
232
233 user =
234 insert(:user,
235 multi_factor_authentication_settings: %MFA.Settings{
236 enabled: true,
237 totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
238 }
239 )
240
241 user2 = insert(:user)
242
243 response =
244 conn
245 |> post(remote_follow_path(conn, :do_follow), %{
246 "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
247 })
248 |> response(200)
249
250 mfa_token = Pleroma.Repo.one(from(q in Pleroma.MFA.Token, where: q.user_id == ^user.id))
251
252 assert response =~ "Two-factor authentication"
253 assert response =~ "Authentication code"
254 assert response =~ mfa_token.token
255 refute user2.follower_address in User.following(user)
256 end
257
258 test "returns error when password is incorrect", %{conn: conn} do
259 otp_secret = TOTP.generate_secret()
260
261 user =
262 insert(:user,
263 multi_factor_authentication_settings: %MFA.Settings{
264 enabled: true,
265 totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
266 }
267 )
268
269 user2 = insert(:user)
270
271 response =
272 conn
273 |> post(remote_follow_path(conn, :do_follow), %{
274 "authorization" => %{"name" => user.nickname, "password" => "test1", "id" => user2.id}
275 })
276 |> response(200)
277
278 assert response =~ "Wrong username or password"
279 refute user2.follower_address in User.following(user)
280 end
281
282 test "follows", %{conn: conn} do
283 otp_secret = TOTP.generate_secret()
284
285 user =
286 insert(:user,
287 multi_factor_authentication_settings: %MFA.Settings{
288 enabled: true,
289 totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
290 }
291 )
292
293 {:ok, %{token: token}} = MFA.Token.create(user)
294
295 user2 = insert(:user)
296 otp_token = TOTP.generate_token(otp_secret)
297
298 conn =
299 conn
300 |> post(
301 remote_follow_path(conn, :do_follow),
302 %{
303 "mfa" => %{"code" => otp_token, "token" => token, "id" => user2.id}
304 }
305 )
306
307 assert redirected_to(conn) == "/users/#{user2.id}"
308 assert user2.follower_address in User.following(user)
309 end
310
311 test "returns error when auth code is incorrect", %{conn: conn} do
312 otp_secret = TOTP.generate_secret()
313
314 user =
315 insert(:user,
316 multi_factor_authentication_settings: %MFA.Settings{
317 enabled: true,
318 totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
319 }
320 )
321
322 {:ok, %{token: token}} = MFA.Token.create(user)
323
324 user2 = insert(:user)
325 otp_token = TOTP.generate_token(TOTP.generate_secret())
326
327 response =
328 conn
329 |> post(
330 remote_follow_path(conn, :do_follow),
331 %{
332 "mfa" => %{"code" => otp_token, "token" => token, "id" => user2.id}
333 }
334 )
335 |> response(200)
336
337 assert response =~ "Wrong authentication code"
338 refute user2.follower_address in User.following(user)
339 end
340 end
341
342 describe "POST /ostatus_subscribe - follow/2 without assigned user " do
343 test "follows", %{conn: conn} do
344 user = insert(:user)
345 user2 = insert(:user)
346
347 conn =
348 conn
349 |> post(remote_follow_path(conn, :do_follow), %{
350 "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
351 })
352
353 assert redirected_to(conn) == "/users/#{user2.id}"
354 assert user2.follower_address in User.following(user)
355 end
356
357 test "returns error when followee not found", %{conn: conn} do
358 user = insert(:user)
359
360 response =
361 conn
362 |> post(remote_follow_path(conn, :do_follow), %{
363 "authorization" => %{"name" => user.nickname, "password" => "test", "id" => "jimm"}
364 })
365 |> response(200)
366
367 assert response =~ "Error following account"
368 end
369
370 test "returns error when login invalid", %{conn: conn} do
371 user = insert(:user)
372
373 response =
374 conn
375 |> post(remote_follow_path(conn, :do_follow), %{
376 "authorization" => %{"name" => "jimm", "password" => "test", "id" => user.id}
377 })
378 |> response(200)
379
380 assert response =~ "Wrong username or password"
381 end
382
383 test "returns error when password invalid", %{conn: conn} do
384 user = insert(:user)
385 user2 = insert(:user)
386
387 response =
388 conn
389 |> post(remote_follow_path(conn, :do_follow), %{
390 "authorization" => %{"name" => user.nickname, "password" => "42", "id" => user2.id}
391 })
392 |> response(200)
393
394 assert response =~ "Wrong username or password"
395 end
396
397 test "returns error when user is blocked", %{conn: conn} do
398 clear_config([:user, :deny_follow_blocked], true)
399 user = insert(:user)
400 user2 = insert(:user)
401 {:ok, _user_block} = Pleroma.User.block(user2, user)
402
403 response =
404 conn
405 |> post(remote_follow_path(conn, :do_follow), %{
406 "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
407 })
408 |> response(200)
409
410 assert response =~ "Error following account"
411 end
412 end
413
414 describe "avatar url" do
415 test "without media proxy" do
416 clear_config([:media_proxy, :enabled], false)
417
418 user =
419 insert(:user, %{
420 local: false,
421 avatar: %{"url" => [%{"href" => "https://remote.org/avatar.png"}]}
422 })
423
424 avatar_url = Pleroma.Web.TwitterAPI.RemoteFollowView.avatar_url(user)
425
426 assert avatar_url == "https://remote.org/avatar.png"
427 end
428
429 test "with media proxy" do
430 clear_config([:media_proxy, :enabled], true)
431
432 user =
433 insert(:user, %{
434 local: false,
435 avatar: %{"url" => [%{"href" => "https://remote.org/avatar.png"}]}
436 })
437
438 avatar_url = Pleroma.Web.TwitterAPI.RemoteFollowView.avatar_url(user)
439 url = Pleroma.Web.Endpoint.url()
440
441 assert String.starts_with?(avatar_url, url)
442 end
443
444 test "local avatar is not proxied" do
445 clear_config([:media_proxy, :enabled], true)
446
447 user =
448 insert(:user, %{
449 local: true,
450 avatar: %{"url" => [%{"href" => "#{Pleroma.Web.Endpoint.url()}/localuser/avatar.png"}]}
451 })
452
453 avatar_url = Pleroma.Web.TwitterAPI.RemoteFollowView.avatar_url(user)
454
455 assert avatar_url == "#{Pleroma.Web.Endpoint.url()}/localuser/avatar.png"
456 end
457 end
458 end