expanding filtration for home timeline
[akkoma] / test / pleroma / web / mastodon_api / controllers / timeline_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.MastodonAPI.TimelineControllerTest do
6 use Pleroma.Web.ConnCase
7
8 import Pleroma.Factory
9 import Tesla.Mock
10
11 alias Pleroma.User
12 alias Pleroma.Web.CommonAPI
13
14 setup do
15 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
16 :ok
17 end
18
19 describe "home" do
20 setup do: oauth_access(["read:statuses"])
21
22 test "does NOT embed account/pleroma/relationship in statuses", %{
23 user: user,
24 conn: conn
25 } do
26 other_user = insert(:user)
27
28 {:ok, _} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"})
29
30 response =
31 conn
32 |> assign(:user, user)
33 |> get("/api/v1/timelines/home")
34 |> json_response_and_validate_schema(200)
35
36 assert Enum.all?(response, fn n ->
37 get_in(n, ["account", "pleroma", "relationship"]) == %{}
38 end)
39 end
40
41 test "the home timeline when the direct messages are excluded", %{user: user, conn: conn} do
42 {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"})
43 {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
44
45 {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
46
47 {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
48
49 conn = get(conn, "/api/v1/timelines/home?exclude_visibilities[]=direct")
50
51 assert status_ids = json_response_and_validate_schema(conn, :ok) |> Enum.map(& &1["id"])
52 assert public_activity.id in status_ids
53 assert unlisted_activity.id in status_ids
54 assert private_activity.id in status_ids
55 refute direct_activity.id in status_ids
56 end
57
58 test "muted emotions", %{user: user, conn: conn} do
59 other_user = insert(:user)
60 {:ok, activity} = CommonAPI.post(user, %{status: "."})
61
62 {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
63 User.mute(user, other_user)
64
65 result =
66 conn
67 |> assign(:user, user)
68 |> get("/api/v1/timelines/home")
69 |> json_response_and_validate_schema(200)
70
71 assert [
72 %{
73 "pleroma" => %{
74 "emoji_reactions" => []
75 }
76 }
77 ] = result
78
79 result =
80 conn
81 |> assign(:user, user)
82 |> get("/api/v1/timelines/home?with_muted=true")
83 |> json_response_and_validate_schema(200)
84
85 assert [
86 %{
87 "pleroma" => %{
88 "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}]
89 }
90 }
91 ] = result
92 end
93
94 test "local/remote filtering", %{conn: conn, user: user} do
95 local = insert(:user)
96 remote = insert(:user, local: false)
97
98 {:ok, user, local} = User.follow(user, local)
99 {:ok, _user, remote} = User.follow(user, remote)
100
101 object1 =
102 insert(:note, %{
103 data: %{
104 "to" => ["https://www.w3.org/ns/activitystreams#Public", User.ap_followers(local)]
105 },
106 user: local
107 })
108
109 activity1 =
110 insert(:note_activity, %{
111 note: object1,
112 recipients: ["https://www.w3.org/ns/activitystreams#Public", User.ap_followers(local)],
113 user: local
114 })
115
116 object2 =
117 insert(:note, %{
118 data: %{
119 "to" => ["https://www.w3.org/ns/activitystreams#Public", User.ap_followers(remote)]
120 },
121 user: remote
122 })
123
124 activity2 =
125 insert(:note_activity, %{
126 note: object2,
127 recipients: ["https://www.w3.org/ns/activitystreams#Public", User.ap_followers(remote)],
128 user: remote,
129 local: false
130 })
131
132 resp1 =
133 conn
134 |> get("/api/v1/timelines/home")
135 |> json_response_and_validate_schema(200)
136
137 without_filter_ids = Enum.map(resp1, & &1["id"])
138
139 assert activity1.id in without_filter_ids
140 assert activity2.id in without_filter_ids
141
142 resp2 =
143 conn
144 |> get("/api/v1/timelines/home?local=true")
145 |> json_response_and_validate_schema(200)
146
147 only_local_ids = Enum.map(resp2, & &1["id"])
148
149 assert activity1.id in only_local_ids
150 refute activity2.id in only_local_ids
151
152 resp3 =
153 conn
154 |> get("/api/v1/timelines/home?only_remote=true")
155 |> json_response_and_validate_schema(200)
156
157 only_remote_ids = Enum.map(resp3, & &1["id"])
158
159 refute activity1.id in only_remote_ids
160 assert activity2.id in only_remote_ids
161
162 resp4 =
163 conn
164 |> get("/api/v1/timelines/home?only_remote=true&local=true")
165 |> json_response_and_validate_schema(200)
166
167 assert resp4 == []
168 end
169 end
170
171 describe "public" do
172 @tag capture_log: true
173 test "the public timeline", %{conn: conn} do
174 user = insert(:user)
175
176 {:ok, activity} = CommonAPI.post(user, %{status: "test"})
177
178 _activity = insert(:note_activity, local: false)
179
180 conn = get(conn, "/api/v1/timelines/public?local=False")
181
182 assert length(json_response_and_validate_schema(conn, :ok)) == 2
183
184 conn = get(build_conn(), "/api/v1/timelines/public?local=True")
185
186 assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok)
187
188 conn = get(build_conn(), "/api/v1/timelines/public?local=1")
189
190 assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok)
191
192 # does not contain repeats
193 {:ok, _} = CommonAPI.repeat(activity.id, user)
194
195 conn = get(build_conn(), "/api/v1/timelines/public?local=true")
196
197 assert [_] = json_response_and_validate_schema(conn, :ok)
198 end
199
200 test "the public timeline includes only public statuses for an authenticated user" do
201 %{user: user, conn: conn} = oauth_access(["read:statuses"])
202
203 {:ok, _activity} = CommonAPI.post(user, %{status: "test"})
204 {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "private"})
205 {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "unlisted"})
206 {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "direct"})
207
208 res_conn = get(conn, "/api/v1/timelines/public")
209 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
210 end
211
212 test "doesn't return replies if follower is posting with blocked user" do
213 %{conn: conn, user: blocker} = oauth_access(["read:statuses"])
214 [blockee, friend] = insert_list(2, :user)
215 {:ok, blocker, friend} = User.follow(blocker, friend)
216 {:ok, _} = User.block(blocker, blockee)
217
218 conn = assign(conn, :user, blocker)
219
220 {:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"})
221
222 {:ok, reply_from_blockee} =
223 CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity})
224
225 {:ok, _reply_from_friend} =
226 CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee})
227
228 # Still shows replies from yourself
229 {:ok, %{id: reply_from_me}} =
230 CommonAPI.post(blocker, %{status: "status", in_reply_to_status_id: reply_from_blockee})
231
232 response =
233 get(conn, "/api/v1/timelines/public")
234 |> json_response_and_validate_schema(200)
235
236 assert length(response) == 2
237 [%{"id" => ^reply_from_me}, %{"id" => ^activity_id}] = response
238 end
239
240 test "doesn't return replies if follow is posting with users from blocked domain" do
241 %{conn: conn, user: blocker} = oauth_access(["read:statuses"])
242 friend = insert(:user)
243 blockee = insert(:user, ap_id: "https://example.com/users/blocked")
244 {:ok, blocker, friend} = User.follow(blocker, friend)
245 {:ok, blocker} = User.block_domain(blocker, "example.com")
246
247 conn = assign(conn, :user, blocker)
248
249 {:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"})
250
251 {:ok, reply_from_blockee} =
252 CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity})
253
254 {:ok, _reply_from_friend} =
255 CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee})
256
257 res_conn = get(conn, "/api/v1/timelines/public")
258
259 activities = json_response_and_validate_schema(res_conn, 200)
260 [%{"id" => ^activity_id}] = activities
261 end
262
263 test "can be filtered by instance", %{conn: conn} do
264 user = insert(:user, ap_id: "https://lain.com/users/lain")
265 insert(:note_activity, local: false)
266 insert(:note_activity, local: false)
267
268 {:ok, _} = CommonAPI.post(user, %{status: "test"})
269
270 conn = get(conn, "/api/v1/timelines/public?instance=lain.com")
271
272 assert length(json_response_and_validate_schema(conn, :ok)) == 1
273 end
274
275 test "muted emotions", %{conn: conn} do
276 user = insert(:user)
277 token = insert(:oauth_token, user: user, scopes: ["read:statuses"])
278
279 conn =
280 conn
281 |> assign(:user, user)
282 |> assign(:token, token)
283
284 other_user = insert(:user)
285 {:ok, activity} = CommonAPI.post(user, %{status: "."})
286
287 {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
288 User.mute(user, other_user)
289
290 result =
291 conn
292 |> get("/api/v1/timelines/public")
293 |> json_response_and_validate_schema(200)
294
295 assert [
296 %{
297 "pleroma" => %{
298 "emoji_reactions" => []
299 }
300 }
301 ] = result
302
303 result =
304 conn
305 |> get("/api/v1/timelines/public?with_muted=true")
306 |> json_response_and_validate_schema(200)
307
308 assert [
309 %{
310 "pleroma" => %{
311 "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}]
312 }
313 }
314 ] = result
315 end
316 end
317
318 defp local_and_remote_activities do
319 insert(:note_activity)
320 insert(:note_activity, local: false)
321 :ok
322 end
323
324 describe "public with restrict unauthenticated timeline for local and federated timelines" do
325 setup do: local_and_remote_activities()
326
327 setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true)
328
329 setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true)
330
331 test "if user is unauthenticated", %{conn: conn} do
332 res_conn = get(conn, "/api/v1/timelines/public?local=true")
333
334 assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
335 "error" => "authorization required for timeline view"
336 }
337
338 res_conn = get(conn, "/api/v1/timelines/public?local=false")
339
340 assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
341 "error" => "authorization required for timeline view"
342 }
343 end
344
345 test "if user is authenticated" do
346 %{conn: conn} = oauth_access(["read:statuses"])
347
348 res_conn = get(conn, "/api/v1/timelines/public?local=true")
349 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
350
351 res_conn = get(conn, "/api/v1/timelines/public?local=false")
352 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
353 end
354 end
355
356 describe "public with restrict unauthenticated timeline for local" do
357 setup do: local_and_remote_activities()
358
359 setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true)
360
361 test "if user is unauthenticated", %{conn: conn} do
362 res_conn = get(conn, "/api/v1/timelines/public?local=true")
363
364 assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
365 "error" => "authorization required for timeline view"
366 }
367
368 res_conn = get(conn, "/api/v1/timelines/public?local=false")
369 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
370 end
371
372 test "if user is authenticated", %{conn: _conn} do
373 %{conn: conn} = oauth_access(["read:statuses"])
374
375 res_conn = get(conn, "/api/v1/timelines/public?local=true")
376 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
377
378 res_conn = get(conn, "/api/v1/timelines/public?local=false")
379 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
380 end
381 end
382
383 describe "public with restrict unauthenticated timeline for remote" do
384 setup do: local_and_remote_activities()
385
386 setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true)
387
388 test "if user is unauthenticated", %{conn: conn} do
389 res_conn = get(conn, "/api/v1/timelines/public?local=true")
390 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
391
392 res_conn = get(conn, "/api/v1/timelines/public?local=false")
393
394 assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
395 "error" => "authorization required for timeline view"
396 }
397 end
398
399 test "if user is authenticated", %{conn: _conn} do
400 %{conn: conn} = oauth_access(["read:statuses"])
401
402 res_conn = get(conn, "/api/v1/timelines/public?local=true")
403 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
404
405 res_conn = get(conn, "/api/v1/timelines/public?local=false")
406 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
407 end
408 end
409
410 describe "direct" do
411 test "direct timeline", %{conn: conn} do
412 user_one = insert(:user)
413 user_two = insert(:user)
414
415 {:ok, user_two, user_one} = User.follow(user_two, user_one)
416
417 {:ok, direct} =
418 CommonAPI.post(user_one, %{
419 status: "Hi @#{user_two.nickname}!",
420 visibility: "direct"
421 })
422
423 {:ok, _follower_only} =
424 CommonAPI.post(user_one, %{
425 status: "Hi @#{user_two.nickname}!",
426 visibility: "private"
427 })
428
429 conn_user_two =
430 conn
431 |> assign(:user, user_two)
432 |> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"]))
433
434 # Only direct should be visible here
435 res_conn = get(conn_user_two, "api/v1/timelines/direct")
436
437 assert [status] = json_response_and_validate_schema(res_conn, :ok)
438
439 assert %{"visibility" => "direct"} = status
440 assert status["url"] != direct.data["id"]
441
442 # User should be able to see their own direct message
443 res_conn =
444 build_conn()
445 |> assign(:user, user_one)
446 |> assign(:token, insert(:oauth_token, user: user_one, scopes: ["read:statuses"]))
447 |> get("api/v1/timelines/direct")
448
449 [status] = json_response_and_validate_schema(res_conn, :ok)
450
451 assert %{"visibility" => "direct"} = status
452
453 # Both should be visible here
454 res_conn = get(conn_user_two, "api/v1/timelines/home")
455
456 [_s1, _s2] = json_response_and_validate_schema(res_conn, :ok)
457
458 # Test pagination
459 Enum.each(1..20, fn _ ->
460 {:ok, _} =
461 CommonAPI.post(user_one, %{
462 status: "Hi @#{user_two.nickname}!",
463 visibility: "direct"
464 })
465 end)
466
467 res_conn = get(conn_user_two, "api/v1/timelines/direct")
468
469 statuses = json_response_and_validate_schema(res_conn, :ok)
470 assert length(statuses) == 20
471
472 max_id = List.last(statuses)["id"]
473
474 res_conn = get(conn_user_two, "api/v1/timelines/direct?max_id=#{max_id}")
475
476 assert [status] = json_response_and_validate_schema(res_conn, :ok)
477
478 assert status["url"] != direct.data["id"]
479 end
480
481 test "doesn't include DMs from blocked users" do
482 %{user: blocker, conn: conn} = oauth_access(["read:statuses"])
483 blocked = insert(:user)
484 other_user = insert(:user)
485 {:ok, _user_relationship} = User.block(blocker, blocked)
486
487 {:ok, _blocked_direct} =
488 CommonAPI.post(blocked, %{
489 status: "Hi @#{blocker.nickname}!",
490 visibility: "direct"
491 })
492
493 {:ok, direct} =
494 CommonAPI.post(other_user, %{
495 status: "Hi @#{blocker.nickname}!",
496 visibility: "direct"
497 })
498
499 res_conn = get(conn, "api/v1/timelines/direct")
500
501 [status] = json_response_and_validate_schema(res_conn, :ok)
502 assert status["id"] == direct.id
503 end
504 end
505
506 describe "list" do
507 setup do: oauth_access(["read:lists"])
508
509 test "does not contain retoots", %{user: user, conn: conn} do
510 other_user = insert(:user)
511 {:ok, activity_one} = CommonAPI.post(user, %{status: "Marisa is cute."})
512 {:ok, activity_two} = CommonAPI.post(other_user, %{status: "Marisa is stupid."})
513 {:ok, _} = CommonAPI.repeat(activity_one.id, other_user)
514
515 {:ok, list} = Pleroma.List.create("name", user)
516 {:ok, list} = Pleroma.List.follow(list, other_user)
517
518 conn = get(conn, "/api/v1/timelines/list/#{list.id}")
519
520 assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok)
521
522 assert id == to_string(activity_two.id)
523 end
524
525 test "works with pagination", %{user: user, conn: conn} do
526 other_user = insert(:user)
527 {:ok, list} = Pleroma.List.create("name", user)
528 {:ok, list} = Pleroma.List.follow(list, other_user)
529
530 Enum.each(1..30, fn i ->
531 CommonAPI.post(other_user, %{status: "post number #{i}"})
532 end)
533
534 res =
535 get(conn, "/api/v1/timelines/list/#{list.id}?limit=1")
536 |> json_response_and_validate_schema(:ok)
537
538 assert length(res) == 1
539
540 [first] = res
541
542 res =
543 get(conn, "/api/v1/timelines/list/#{list.id}?max_id=#{first["id"]}&limit=30")
544 |> json_response_and_validate_schema(:ok)
545
546 assert length(res) == 29
547 end
548
549 test "list timeline", %{user: user, conn: conn} do
550 other_user = insert(:user)
551 {:ok, _activity_one} = CommonAPI.post(user, %{status: "Marisa is cute."})
552 {:ok, activity_two} = CommonAPI.post(other_user, %{status: "Marisa is cute."})
553 {:ok, list} = Pleroma.List.create("name", user)
554 {:ok, list} = Pleroma.List.follow(list, other_user)
555
556 conn = get(conn, "/api/v1/timelines/list/#{list.id}")
557
558 assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok)
559
560 assert id == to_string(activity_two.id)
561 end
562
563 test "list timeline does not leak non-public statuses for unfollowed users", %{
564 user: user,
565 conn: conn
566 } do
567 other_user = insert(:user)
568 {:ok, activity_one} = CommonAPI.post(other_user, %{status: "Marisa is cute."})
569
570 {:ok, _activity_two} =
571 CommonAPI.post(other_user, %{
572 status: "Marisa is cute.",
573 visibility: "private"
574 })
575
576 {:ok, list} = Pleroma.List.create("name", user)
577 {:ok, list} = Pleroma.List.follow(list, other_user)
578
579 conn = get(conn, "/api/v1/timelines/list/#{list.id}")
580
581 assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok)
582
583 assert id == to_string(activity_one.id)
584 end
585
586 test "muted emotions", %{user: user, conn: conn} do
587 user2 = insert(:user)
588 user3 = insert(:user)
589 {:ok, activity} = CommonAPI.post(user2, %{status: "."})
590
591 {:ok, _} = CommonAPI.react_with_emoji(activity.id, user3, "🎅")
592 User.mute(user, user3)
593
594 {:ok, list} = Pleroma.List.create("name", user)
595 {:ok, list} = Pleroma.List.follow(list, user2)
596
597 result =
598 conn
599 |> get("/api/v1/timelines/list/#{list.id}")
600 |> json_response_and_validate_schema(200)
601
602 assert [
603 %{
604 "pleroma" => %{
605 "emoji_reactions" => []
606 }
607 }
608 ] = result
609
610 result =
611 conn
612 |> get("/api/v1/timelines/list/#{list.id}?with_muted=true")
613 |> json_response_and_validate_schema(200)
614
615 assert [
616 %{
617 "pleroma" => %{
618 "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}]
619 }
620 }
621 ] = result
622 end
623 end
624
625 describe "hashtag" do
626 setup do: oauth_access(["n/a"])
627
628 @tag capture_log: true
629 test "hashtag timeline", %{conn: conn} do
630 following = insert(:user)
631
632 {:ok, activity} = CommonAPI.post(following, %{status: "test #2hu"})
633
634 nconn = get(conn, "/api/v1/timelines/tag/2hu")
635
636 assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok)
637
638 assert id == to_string(activity.id)
639
640 # works for different capitalization too
641 nconn = get(conn, "/api/v1/timelines/tag/2HU")
642
643 assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok)
644
645 assert id == to_string(activity.id)
646 end
647
648 test "multi-hashtag timeline", %{conn: conn} do
649 user = insert(:user)
650
651 {:ok, activity_test} = CommonAPI.post(user, %{status: "#test"})
652 {:ok, activity_test1} = CommonAPI.post(user, %{status: "#test #test1"})
653 {:ok, activity_none} = CommonAPI.post(user, %{status: "#test #none"})
654
655 any_test = get(conn, "/api/v1/timelines/tag/test?any[]=test1")
656
657 [status_none, status_test1, status_test] = json_response_and_validate_schema(any_test, :ok)
658
659 assert to_string(activity_test.id) == status_test["id"]
660 assert to_string(activity_test1.id) == status_test1["id"]
661 assert to_string(activity_none.id) == status_none["id"]
662
663 restricted_test = get(conn, "/api/v1/timelines/tag/test?all[]=test1&none[]=none")
664
665 assert [status_test1] == json_response_and_validate_schema(restricted_test, :ok)
666
667 all_test = get(conn, "/api/v1/timelines/tag/test?all[]=none")
668
669 assert [status_none] == json_response_and_validate_schema(all_test, :ok)
670 end
671
672 test "muted emotions", %{conn: conn} do
673 user = insert(:user)
674 token = insert(:oauth_token, user: user, scopes: ["read:statuses"])
675
676 conn =
677 conn
678 |> assign(:user, user)
679 |> assign(:token, token)
680
681 other_user = insert(:user)
682 {:ok, activity} = CommonAPI.post(user, %{status: "test #2hu"})
683
684 {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
685 User.mute(user, other_user)
686
687 result =
688 conn
689 |> get("/api/v1/timelines/tag/2hu")
690 |> json_response_and_validate_schema(200)
691
692 assert [
693 %{
694 "pleroma" => %{
695 "emoji_reactions" => []
696 }
697 }
698 ] = result
699
700 result =
701 conn
702 |> get("/api/v1/timelines/tag/2hu?with_muted=true")
703 |> json_response_and_validate_schema(200)
704
705 assert [
706 %{
707 "pleroma" => %{
708 "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}]
709 }
710 }
711 ] = result
712 end
713 end
714
715 describe "hashtag timeline handling of :restrict_unauthenticated setting" do
716 setup do
717 user = insert(:user)
718 {:ok, activity1} = CommonAPI.post(user, %{status: "test #tag1"})
719 {:ok, _activity2} = CommonAPI.post(user, %{status: "test #tag1"})
720
721 activity1
722 |> Ecto.Changeset.change(%{local: false})
723 |> Pleroma.Repo.update()
724
725 base_uri = "/api/v1/timelines/tag/tag1"
726 error_response = %{"error" => "authorization required for timeline view"}
727
728 %{base_uri: base_uri, error_response: error_response}
729 end
730
731 defp ensure_authenticated_access(base_uri) do
732 %{conn: auth_conn} = oauth_access(["read:statuses"])
733
734 res_conn = get(auth_conn, "#{base_uri}?local=true")
735 assert length(json_response(res_conn, 200)) == 1
736
737 res_conn = get(auth_conn, "#{base_uri}?local=false")
738 assert length(json_response(res_conn, 200)) == 2
739 end
740
741 test "with default settings on private instances, returns 403 for unauthenticated users", %{
742 conn: conn,
743 base_uri: base_uri,
744 error_response: error_response
745 } do
746 clear_config([:instance, :public], false)
747 clear_config([:restrict_unauthenticated, :timelines])
748
749 for local <- [true, false] do
750 res_conn = get(conn, "#{base_uri}?local=#{local}")
751
752 assert json_response(res_conn, :unauthorized) == error_response
753 end
754
755 ensure_authenticated_access(base_uri)
756 end
757
758 test "with `%{local: true, federated: true}`, returns 403 for unauthenticated users", %{
759 conn: conn,
760 base_uri: base_uri,
761 error_response: error_response
762 } do
763 clear_config([:restrict_unauthenticated, :timelines, :local], true)
764 clear_config([:restrict_unauthenticated, :timelines, :federated], true)
765
766 for local <- [true, false] do
767 res_conn = get(conn, "#{base_uri}?local=#{local}")
768
769 assert json_response(res_conn, :unauthorized) == error_response
770 end
771
772 ensure_authenticated_access(base_uri)
773 end
774
775 test "with `%{local: false, federated: true}`, forbids unauthenticated access to federated timeline",
776 %{conn: conn, base_uri: base_uri, error_response: error_response} do
777 clear_config([:restrict_unauthenticated, :timelines, :local], false)
778 clear_config([:restrict_unauthenticated, :timelines, :federated], true)
779
780 res_conn = get(conn, "#{base_uri}?local=true")
781 assert length(json_response(res_conn, 200)) == 1
782
783 res_conn = get(conn, "#{base_uri}?local=false")
784 assert json_response(res_conn, :unauthorized) == error_response
785
786 ensure_authenticated_access(base_uri)
787 end
788
789 test "with `%{local: true, federated: false}`, forbids unauthenticated access to public timeline" <>
790 "(but not to local public activities which are delivered as part of federated timeline)",
791 %{conn: conn, base_uri: base_uri, error_response: error_response} do
792 clear_config([:restrict_unauthenticated, :timelines, :local], true)
793 clear_config([:restrict_unauthenticated, :timelines, :federated], false)
794
795 res_conn = get(conn, "#{base_uri}?local=true")
796 assert json_response(res_conn, :unauthorized) == error_response
797
798 # Note: local activities get delivered as part of federated timeline
799 res_conn = get(conn, "#{base_uri}?local=false")
800 assert length(json_response(res_conn, 200)) == 2
801
802 ensure_authenticated_access(base_uri)
803 end
804 end
805 end