remove public post quarantine exception (#114)
[akkoma] / test / pleroma / web / activity_pub / publisher_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.ActivityPub.PublisherTest do
6 use Pleroma.Web.ConnCase
7
8 import ExUnit.CaptureLog
9 import Pleroma.Factory
10 import Tesla.Mock
11 import Mock
12
13 alias Pleroma.Activity
14 alias Pleroma.Instances
15 alias Pleroma.Object
16 alias Pleroma.Web.ActivityPub.Publisher
17 alias Pleroma.Web.CommonAPI
18
19 @as_public "https://www.w3.org/ns/activitystreams#Public"
20
21 setup do
22 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
23 :ok
24 end
25
26 setup_all do
27 clear_config([:instance, :federating], true)
28 clear_config([:instance, :quarantined_instances], [])
29 end
30
31 describe "gather_webfinger_links/1" do
32 test "it returns links" do
33 user = insert(:user)
34
35 expected_links = [
36 %{"href" => user.ap_id, "rel" => "self", "type" => "application/activity+json"},
37 %{
38 "href" => user.ap_id,
39 "rel" => "self",
40 "type" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
41 },
42 %{
43 "rel" => "http://ostatus.org/schema/1.0/subscribe",
44 "template" => "#{Pleroma.Web.Endpoint.url()}/ostatus_subscribe?acct={uri}"
45 }
46 ]
47
48 assert expected_links == Publisher.gather_webfinger_links(user)
49 end
50 end
51
52 describe "determine_inbox/2" do
53 test "it returns sharedInbox for messages involving as:Public in to" do
54 user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
55
56 activity = %Activity{
57 data: %{"to" => [@as_public], "cc" => [user.follower_address]}
58 }
59
60 assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
61 end
62
63 test "it returns sharedInbox for messages involving as:Public in cc" do
64 user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
65
66 activity = %Activity{
67 data: %{"cc" => [@as_public], "to" => [user.follower_address]}
68 }
69
70 assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
71 end
72
73 test "it returns sharedInbox for messages involving multiple recipients in to" do
74 user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
75 user_two = insert(:user)
76 user_three = insert(:user)
77
78 activity = %Activity{
79 data: %{"cc" => [], "to" => [user.ap_id, user_two.ap_id, user_three.ap_id]}
80 }
81
82 assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
83 end
84
85 test "it returns sharedInbox for messages involving multiple recipients in cc" do
86 user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
87 user_two = insert(:user)
88 user_three = insert(:user)
89
90 activity = %Activity{
91 data: %{"to" => [], "cc" => [user.ap_id, user_two.ap_id, user_three.ap_id]}
92 }
93
94 assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
95 end
96
97 test "it returns sharedInbox for messages involving multiple recipients in total" do
98 user =
99 insert(:user, %{
100 shared_inbox: "http://example.com/inbox",
101 inbox: "http://example.com/personal-inbox"
102 })
103
104 user_two = insert(:user)
105
106 activity = %Activity{
107 data: %{"to" => [user_two.ap_id], "cc" => [user.ap_id]}
108 }
109
110 assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
111 end
112
113 test "it returns inbox for messages involving single recipients in total" do
114 user =
115 insert(:user, %{
116 shared_inbox: "http://example.com/inbox",
117 inbox: "http://example.com/personal-inbox"
118 })
119
120 activity = %Activity{
121 data: %{"to" => [user.ap_id], "cc" => []}
122 }
123
124 assert Publisher.determine_inbox(activity, user) == "http://example.com/personal-inbox"
125 end
126 end
127
128 describe "publish_one/1" do
129 test "publish to url with with different ports" do
130 inbox80 = "http://42.site/users/nick1/inbox"
131 inbox42 = "http://42.site:42/users/nick1/inbox"
132
133 mock(fn
134 %{method: :post, url: "http://42.site:42/users/nick1/inbox"} ->
135 {:ok, %Tesla.Env{status: 200, body: "port 42"}}
136
137 %{method: :post, url: "http://42.site/users/nick1/inbox"} ->
138 {:ok, %Tesla.Env{status: 200, body: "port 80"}}
139 end)
140
141 actor = insert(:user)
142
143 assert {:ok, %{body: "port 42"}} =
144 Publisher.publish_one(%{
145 inbox: inbox42,
146 json: "{}",
147 actor: actor,
148 id: 1,
149 unreachable_since: true
150 })
151
152 assert {:ok, %{body: "port 80"}} =
153 Publisher.publish_one(%{
154 inbox: inbox80,
155 json: "{}",
156 actor: actor,
157 id: 1,
158 unreachable_since: true
159 })
160 end
161
162 test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified",
163 Instances,
164 [:passthrough],
165 [] do
166 actor = insert(:user)
167 inbox = "http://200.site/users/nick1/inbox"
168
169 assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
170 assert called(Instances.set_reachable(inbox))
171 end
172
173 test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is set",
174 Instances,
175 [:passthrough],
176 [] do
177 actor = insert(:user)
178 inbox = "http://200.site/users/nick1/inbox"
179
180 assert {:ok, _} =
181 Publisher.publish_one(%{
182 inbox: inbox,
183 json: "{}",
184 actor: actor,
185 id: 1,
186 unreachable_since: NaiveDateTime.utc_now()
187 })
188
189 assert called(Instances.set_reachable(inbox))
190 end
191
192 test_with_mock "does NOT call `Instances.set_reachable` on successful federation if `unreachable_since` is nil",
193 Instances,
194 [:passthrough],
195 [] do
196 actor = insert(:user)
197 inbox = "http://200.site/users/nick1/inbox"
198
199 assert {:ok, _} =
200 Publisher.publish_one(%{
201 inbox: inbox,
202 json: "{}",
203 actor: actor,
204 id: 1,
205 unreachable_since: nil
206 })
207
208 refute called(Instances.set_reachable(inbox))
209 end
210
211 test_with_mock "calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code",
212 Instances,
213 [:passthrough],
214 [] do
215 actor = insert(:user)
216 inbox = "http://404.site/users/nick1/inbox"
217
218 assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
219
220 assert called(Instances.set_unreachable(inbox))
221 end
222
223 test_with_mock "it calls `Instances.set_unreachable` on target inbox on request error of any kind",
224 Instances,
225 [:passthrough],
226 [] do
227 actor = insert(:user)
228 inbox = "http://connrefused.site/users/nick1/inbox"
229
230 assert capture_log(fn ->
231 assert {:error, _} =
232 Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
233 end) =~ "connrefused"
234
235 assert called(Instances.set_unreachable(inbox))
236 end
237
238 test_with_mock "does NOT call `Instances.set_unreachable` if target is reachable",
239 Instances,
240 [:passthrough],
241 [] do
242 actor = insert(:user)
243 inbox = "http://200.site/users/nick1/inbox"
244
245 assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
246
247 refute called(Instances.set_unreachable(inbox))
248 end
249
250 test_with_mock "does NOT call `Instances.set_unreachable` if target instance has non-nil `unreachable_since`",
251 Instances,
252 [:passthrough],
253 [] do
254 actor = insert(:user)
255 inbox = "http://connrefused.site/users/nick1/inbox"
256
257 assert capture_log(fn ->
258 assert {:error, _} =
259 Publisher.publish_one(%{
260 inbox: inbox,
261 json: "{}",
262 actor: actor,
263 id: 1,
264 unreachable_since: NaiveDateTime.utc_now()
265 })
266 end) =~ "connrefused"
267
268 refute called(Instances.set_unreachable(inbox))
269 end
270 end
271
272 describe "publish/2" do
273 test_with_mock "doesn't publish any activity to quarantined instances.",
274 Pleroma.Web.Federator.Publisher,
275 [:passthrough],
276 [] do
277 Config.put([:instance, :quarantined_instances], [{"domain.com", "some reason"}])
278
279 follower =
280 insert(:user, %{
281 local: false,
282 inbox: "https://domain.com/users/nick1/inbox",
283 ap_enabled: true
284 })
285
286 actor = insert(:user, follower_address: follower.ap_id)
287
288 {:ok, follower, actor} = Pleroma.User.follow(follower, actor)
289 actor = refresh_record(actor)
290
291 note_activity =
292 insert(:followers_only_note_activity,
293 user: actor,
294 recipients: [follower.ap_id]
295 )
296
297 public_note_activity =
298 insert(:note_activity,
299 user: actor,
300 recipients: [follower.ap_id, @as_public]
301 )
302
303 res = Publisher.publish(actor, note_activity)
304
305 assert res == :ok
306
307 :ok = Publisher.publish(actor, public_note_activity)
308
309 assert not called(
310 Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
311 inbox: "https://domain.com/users/nick1/inbox",
312 actor_id: actor.id,
313 id: note_activity.data["id"]
314 })
315 )
316
317 assert not called(
318 Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
319 inbox: "https://domain.com/users/nick1/inbox",
320 actor_id: actor.id,
321 id: public_note_activity.data["id"]
322 })
323 )
324 end
325
326 test_with_mock "Publishes a non-public activity to non-quarantined instances.",
327 Pleroma.Web.Federator.Publisher,
328 [:passthrough],
329 [] do
330 Config.put([:instance, :quarantined_instances], [{"somedomain.com", "some reason"}])
331
332 follower =
333 insert(:user, %{
334 local: false,
335 inbox: "https://domain.com/users/nick1/inbox",
336 ap_enabled: true
337 })
338
339 actor = insert(:user, follower_address: follower.ap_id)
340
341 {:ok, follower, actor} = Pleroma.User.follow(follower, actor)
342 actor = refresh_record(actor)
343
344 note_activity =
345 insert(:followers_only_note_activity,
346 user: actor,
347 recipients: [follower.ap_id]
348 )
349
350 res = Publisher.publish(actor, note_activity)
351
352 assert res == :ok
353
354 assert called(
355 Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
356 inbox: "https://domain.com/users/nick1/inbox",
357 actor_id: actor.id,
358 id: note_activity.data["id"]
359 })
360 )
361 end
362
363 test_with_mock "publishes an activity with BCC to all relevant peers.",
364 Pleroma.Web.Federator.Publisher,
365 [:passthrough],
366 [] do
367 Config.put([:instance, :quarantined_instances], [])
368
369 follower =
370 insert(:user, %{
371 local: false,
372 inbox: "https://domain.com/users/nick1/inbox",
373 ap_enabled: true
374 })
375
376 actor = insert(:user, follower_address: follower.ap_id)
377 user = insert(:user)
378
379 {:ok, follower, actor} = Pleroma.User.follow(follower, actor)
380
381 note_activity =
382 insert(:note_activity,
383 recipients: [follower.ap_id],
384 data_attrs: %{"bcc" => [user.ap_id]}
385 )
386
387 res = Publisher.publish(actor, note_activity)
388 assert res == :ok
389
390 assert called(
391 Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
392 inbox: "https://domain.com/users/nick1/inbox",
393 actor_id: actor.id,
394 id: note_activity.data["id"]
395 })
396 )
397 end
398
399 test_with_mock "publishes a delete activity to peers who signed fetch requests to the create acitvity/object.",
400 Pleroma.Web.Federator.Publisher,
401 [:passthrough],
402 [] do
403 clear_config([:instance, :quarantined_instances], [])
404
405 fetcher =
406 insert(:user,
407 local: false,
408 inbox: "https://domain.com/users/nick1/inbox",
409 ap_enabled: true
410 )
411
412 another_fetcher =
413 insert(:user,
414 local: false,
415 inbox: "https://domain2.com/users/nick1/inbox",
416 ap_enabled: true
417 )
418
419 actor = insert(:user)
420
421 note_activity = insert(:note_activity, user: actor)
422 object = Object.normalize(note_activity, fetch: false)
423
424 activity_path = String.trim_leading(note_activity.data["id"], Pleroma.Web.Endpoint.url())
425 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
426
427 build_conn()
428 |> put_req_header("accept", "application/activity+json")
429 |> assign(:user, fetcher)
430 |> get(object_path)
431 |> json_response(200)
432
433 build_conn()
434 |> put_req_header("accept", "application/activity+json")
435 |> assign(:user, another_fetcher)
436 |> get(activity_path)
437 |> json_response(200)
438
439 {:ok, delete} = CommonAPI.delete(note_activity.id, actor)
440
441 res = Publisher.publish(actor, delete)
442 assert res == :ok
443
444 assert called(
445 Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
446 inbox: "https://domain.com/users/nick1/inbox",
447 actor_id: actor.id,
448 id: delete.data["id"]
449 })
450 )
451
452 assert called(
453 Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
454 inbox: "https://domain2.com/users/nick1/inbox",
455 actor_id: actor.id,
456 id: delete.data["id"]
457 })
458 )
459 end
460 end
461 end