Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

1672 rindas
50KB

  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. defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
  5. use Pleroma.Web.ConnCase
  6. use Oban.Testing, repo: Pleroma.Repo
  7. alias Pleroma.Activity
  8. alias Pleroma.Delivery
  9. alias Pleroma.Instances
  10. alias Pleroma.Object
  11. alias Pleroma.Tests.ObanHelpers
  12. alias Pleroma.User
  13. alias Pleroma.Web.ActivityPub.ActivityPub
  14. alias Pleroma.Web.ActivityPub.ObjectView
  15. alias Pleroma.Web.ActivityPub.Relay
  16. alias Pleroma.Web.ActivityPub.UserView
  17. alias Pleroma.Web.ActivityPub.Utils
  18. alias Pleroma.Web.CommonAPI
  19. alias Pleroma.Web.Endpoint
  20. alias Pleroma.Workers.ReceiverWorker
  21. import Pleroma.Factory
  22. require Pleroma.Constants
  23. setup_all do
  24. Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
  25. :ok
  26. end
  27. setup do: clear_config([:instance, :federating], true)
  28. describe "/relay" do
  29. setup do: clear_config([:instance, :allow_relay])
  30. test "with the relay active, it returns the relay user", %{conn: conn} do
  31. res =
  32. conn
  33. |> get(activity_pub_path(conn, :relay))
  34. |> json_response(200)
  35. assert res["id"] =~ "/relay"
  36. end
  37. test "with the relay disabled, it returns 404", %{conn: conn} do
  38. clear_config([:instance, :allow_relay], false)
  39. conn
  40. |> get(activity_pub_path(conn, :relay))
  41. |> json_response(404)
  42. end
  43. test "on non-federating instance, it returns 404", %{conn: conn} do
  44. clear_config([:instance, :federating], false)
  45. user = insert(:user)
  46. conn
  47. |> assign(:user, user)
  48. |> get(activity_pub_path(conn, :relay))
  49. |> json_response(404)
  50. end
  51. end
  52. describe "/internal/fetch" do
  53. test "it returns the internal fetch user", %{conn: conn} do
  54. res =
  55. conn
  56. |> get(activity_pub_path(conn, :internal_fetch))
  57. |> json_response(200)
  58. assert res["id"] =~ "/fetch"
  59. end
  60. test "on non-federating instance, it returns 404", %{conn: conn} do
  61. clear_config([:instance, :federating], false)
  62. user = insert(:user)
  63. conn
  64. |> assign(:user, user)
  65. |> get(activity_pub_path(conn, :internal_fetch))
  66. |> json_response(404)
  67. end
  68. end
  69. describe "/users/:nickname" do
  70. test "it returns a json representation of the user with accept application/json", %{
  71. conn: conn
  72. } do
  73. user = insert(:user)
  74. conn =
  75. conn
  76. |> put_req_header("accept", "application/json")
  77. |> get("/users/#{user.nickname}")
  78. user = User.get_cached_by_id(user.id)
  79. assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
  80. end
  81. test "it returns a json representation of the user with accept application/activity+json", %{
  82. conn: conn
  83. } do
  84. user = insert(:user)
  85. conn =
  86. conn
  87. |> put_req_header("accept", "application/activity+json")
  88. |> get("/users/#{user.nickname}")
  89. user = User.get_cached_by_id(user.id)
  90. assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
  91. end
  92. test "it returns a json representation of the user with accept application/ld+json", %{
  93. conn: conn
  94. } do
  95. user = insert(:user)
  96. conn =
  97. conn
  98. |> put_req_header(
  99. "accept",
  100. "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
  101. )
  102. |> get("/users/#{user.nickname}")
  103. user = User.get_cached_by_id(user.id)
  104. assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
  105. end
  106. test "it returns 404 for remote users", %{
  107. conn: conn
  108. } do
  109. user = insert(:user, local: false, nickname: "remoteuser@example.com")
  110. conn =
  111. conn
  112. |> put_req_header("accept", "application/json")
  113. |> get("/users/#{user.nickname}.json")
  114. assert json_response(conn, 404)
  115. end
  116. test "it returns error when user is not found", %{conn: conn} do
  117. response =
  118. conn
  119. |> put_req_header("accept", "application/json")
  120. |> get("/users/jimm")
  121. |> json_response(404)
  122. assert response == "Not found"
  123. end
  124. end
  125. describe "mastodon compatibility routes" do
  126. test "it returns a json representation of the object with accept application/json", %{
  127. conn: conn
  128. } do
  129. {:ok, object} =
  130. %{
  131. "type" => "Note",
  132. "content" => "hey",
  133. "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999",
  134. "actor" => Endpoint.url() <> "/users/raymoo",
  135. "to" => [Pleroma.Constants.as_public()]
  136. }
  137. |> Object.create()
  138. conn =
  139. conn
  140. |> put_req_header("accept", "application/json")
  141. |> get("/users/raymoo/statuses/999999999")
  142. assert json_response(conn, 200) == ObjectView.render("object.json", %{object: object})
  143. end
  144. test "it returns a json representation of the activity with accept application/json", %{
  145. conn: conn
  146. } do
  147. {:ok, object} =
  148. %{
  149. "type" => "Note",
  150. "content" => "hey",
  151. "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999",
  152. "actor" => Endpoint.url() <> "/users/raymoo",
  153. "to" => [Pleroma.Constants.as_public()]
  154. }
  155. |> Object.create()
  156. {:ok, activity, _} =
  157. %{
  158. "id" => object.data["id"] <> "/activity",
  159. "type" => "Create",
  160. "object" => object.data["id"],
  161. "actor" => object.data["actor"],
  162. "to" => object.data["to"]
  163. }
  164. |> ActivityPub.persist(local: true)
  165. conn =
  166. conn
  167. |> put_req_header("accept", "application/json")
  168. |> get("/users/raymoo/statuses/999999999/activity")
  169. assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity})
  170. end
  171. end
  172. describe "/objects/:uuid" do
  173. test "it doesn't return a local-only object", %{conn: conn} do
  174. user = insert(:user)
  175. {:ok, post} = CommonAPI.post(user, %{status: "test", visibility: "local"})
  176. assert Pleroma.Web.ActivityPub.Visibility.is_local_public?(post)
  177. object = Object.normalize(post, fetch: false)
  178. uuid = String.split(object.data["id"], "/") |> List.last()
  179. conn =
  180. conn
  181. |> put_req_header("accept", "application/json")
  182. |> get("/objects/#{uuid}")
  183. assert json_response(conn, 404)
  184. end
  185. test "it returns a json representation of the object with accept application/json", %{
  186. conn: conn
  187. } do
  188. note = insert(:note)
  189. uuid = String.split(note.data["id"], "/") |> List.last()
  190. conn =
  191. conn
  192. |> put_req_header("accept", "application/json")
  193. |> get("/objects/#{uuid}")
  194. assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
  195. end
  196. test "it returns a json representation of the object with accept application/activity+json",
  197. %{conn: conn} do
  198. note = insert(:note)
  199. uuid = String.split(note.data["id"], "/") |> List.last()
  200. conn =
  201. conn
  202. |> put_req_header("accept", "application/activity+json")
  203. |> get("/objects/#{uuid}")
  204. assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
  205. end
  206. test "it returns a json representation of the object with accept application/ld+json", %{
  207. conn: conn
  208. } do
  209. note = insert(:note)
  210. uuid = String.split(note.data["id"], "/") |> List.last()
  211. conn =
  212. conn
  213. |> put_req_header(
  214. "accept",
  215. "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
  216. )
  217. |> get("/objects/#{uuid}")
  218. assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
  219. end
  220. test "it returns 404 for non-public messages", %{conn: conn} do
  221. note = insert(:direct_note)
  222. uuid = String.split(note.data["id"], "/") |> List.last()
  223. conn =
  224. conn
  225. |> put_req_header("accept", "application/activity+json")
  226. |> get("/objects/#{uuid}")
  227. assert json_response(conn, 404)
  228. end
  229. test "it returns 404 for tombstone objects", %{conn: conn} do
  230. tombstone = insert(:tombstone)
  231. uuid = String.split(tombstone.data["id"], "/") |> List.last()
  232. conn =
  233. conn
  234. |> put_req_header("accept", "application/activity+json")
  235. |> get("/objects/#{uuid}")
  236. assert json_response(conn, 404)
  237. end
  238. test "it caches a response", %{conn: conn} do
  239. note = insert(:note)
  240. uuid = String.split(note.data["id"], "/") |> List.last()
  241. conn1 =
  242. conn
  243. |> put_req_header("accept", "application/activity+json")
  244. |> get("/objects/#{uuid}")
  245. assert json_response(conn1, :ok)
  246. assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
  247. conn2 =
  248. conn
  249. |> put_req_header("accept", "application/activity+json")
  250. |> get("/objects/#{uuid}")
  251. assert json_response(conn1, :ok) == json_response(conn2, :ok)
  252. assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"}))
  253. end
  254. test "cached purged after object deletion", %{conn: conn} do
  255. note = insert(:note)
  256. uuid = String.split(note.data["id"], "/") |> List.last()
  257. conn1 =
  258. conn
  259. |> put_req_header("accept", "application/activity+json")
  260. |> get("/objects/#{uuid}")
  261. assert json_response(conn1, :ok)
  262. assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
  263. Object.delete(note)
  264. conn2 =
  265. conn
  266. |> put_req_header("accept", "application/activity+json")
  267. |> get("/objects/#{uuid}")
  268. assert "Not found" == json_response(conn2, :not_found)
  269. end
  270. end
  271. describe "/activities/:uuid" do
  272. test "it doesn't return a local-only activity", %{conn: conn} do
  273. user = insert(:user)
  274. {:ok, post} = CommonAPI.post(user, %{status: "test", visibility: "local"})
  275. assert Pleroma.Web.ActivityPub.Visibility.is_local_public?(post)
  276. uuid = String.split(post.data["id"], "/") |> List.last()
  277. conn =
  278. conn
  279. |> put_req_header("accept", "application/json")
  280. |> get("/activities/#{uuid}")
  281. assert json_response(conn, 404)
  282. end
  283. test "it returns a json representation of the activity", %{conn: conn} do
  284. activity = insert(:note_activity)
  285. uuid = String.split(activity.data["id"], "/") |> List.last()
  286. conn =
  287. conn
  288. |> put_req_header("accept", "application/activity+json")
  289. |> get("/activities/#{uuid}")
  290. assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity})
  291. end
  292. test "it returns 404 for non-public activities", %{conn: conn} do
  293. activity = insert(:direct_note_activity)
  294. uuid = String.split(activity.data["id"], "/") |> List.last()
  295. conn =
  296. conn
  297. |> put_req_header("accept", "application/activity+json")
  298. |> get("/activities/#{uuid}")
  299. assert json_response(conn, 404)
  300. end
  301. test "it caches a response", %{conn: conn} do
  302. activity = insert(:note_activity)
  303. uuid = String.split(activity.data["id"], "/") |> List.last()
  304. conn1 =
  305. conn
  306. |> put_req_header("accept", "application/activity+json")
  307. |> get("/activities/#{uuid}")
  308. assert json_response(conn1, :ok)
  309. assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
  310. conn2 =
  311. conn
  312. |> put_req_header("accept", "application/activity+json")
  313. |> get("/activities/#{uuid}")
  314. assert json_response(conn1, :ok) == json_response(conn2, :ok)
  315. assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"}))
  316. end
  317. test "cached purged after activity deletion", %{conn: conn} do
  318. user = insert(:user)
  319. {:ok, activity} = CommonAPI.post(user, %{status: "cofe"})
  320. uuid = String.split(activity.data["id"], "/") |> List.last()
  321. conn1 =
  322. conn
  323. |> put_req_header("accept", "application/activity+json")
  324. |> get("/activities/#{uuid}")
  325. assert json_response(conn1, :ok)
  326. assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
  327. Activity.delete_all_by_object_ap_id(activity.object.data["id"])
  328. conn2 =
  329. conn
  330. |> put_req_header("accept", "application/activity+json")
  331. |> get("/activities/#{uuid}")
  332. assert "Not found" == json_response(conn2, :not_found)
  333. end
  334. end
  335. describe "/inbox" do
  336. test "it inserts an incoming activity into the database", %{conn: conn} do
  337. data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
  338. conn =
  339. conn
  340. |> assign(:valid_signature, true)
  341. |> put_req_header("content-type", "application/activity+json")
  342. |> post("/inbox", data)
  343. assert "ok" == json_response(conn, 200)
  344. ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
  345. assert Activity.get_by_ap_id(data["id"])
  346. end
  347. @tag capture_log: true
  348. test "it inserts an incoming activity into the database" <>
  349. "even if we can't fetch the user but have it in our db",
  350. %{conn: conn} do
  351. user =
  352. insert(:user,
  353. ap_id: "https://mastodon.example.org/users/raymoo",
  354. ap_enabled: true,
  355. local: false,
  356. last_refreshed_at: nil
  357. )
  358. data =
  359. File.read!("test/fixtures/mastodon-post-activity.json")
  360. |> Jason.decode!()
  361. |> Map.put("actor", user.ap_id)
  362. |> put_in(["object", "attridbutedTo"], user.ap_id)
  363. conn =
  364. conn
  365. |> assign(:valid_signature, true)
  366. |> put_req_header("content-type", "application/activity+json")
  367. |> post("/inbox", data)
  368. assert "ok" == json_response(conn, 200)
  369. ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
  370. assert Activity.get_by_ap_id(data["id"])
  371. end
  372. test "it clears `unreachable` federation status of the sender", %{conn: conn} do
  373. data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
  374. sender_url = data["actor"]
  375. Instances.set_consistently_unreachable(sender_url)
  376. refute Instances.reachable?(sender_url)
  377. conn =
  378. conn
  379. |> assign(:valid_signature, true)
  380. |> put_req_header("content-type", "application/activity+json")
  381. |> post("/inbox", data)
  382. assert "ok" == json_response(conn, 200)
  383. assert Instances.reachable?(sender_url)
  384. end
  385. test "accept follow activity", %{conn: conn} do
  386. clear_config([:instance, :federating], true)
  387. relay = Relay.get_actor()
  388. assert {:ok, %Activity{} = activity} = Relay.follow("https://relay.mastodon.host/actor")
  389. followed_relay = Pleroma.User.get_by_ap_id("https://relay.mastodon.host/actor")
  390. relay = refresh_record(relay)
  391. accept =
  392. File.read!("test/fixtures/relay/accept-follow.json")
  393. |> String.replace("{{ap_id}}", relay.ap_id)
  394. |> String.replace("{{activity_id}}", activity.data["id"])
  395. assert "ok" ==
  396. conn
  397. |> assign(:valid_signature, true)
  398. |> put_req_header("content-type", "application/activity+json")
  399. |> post("/inbox", accept)
  400. |> json_response(200)
  401. ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
  402. assert Pleroma.FollowingRelationship.following?(
  403. relay,
  404. followed_relay
  405. )
  406. Mix.shell(Mix.Shell.Process)
  407. on_exit(fn ->
  408. Mix.shell(Mix.Shell.IO)
  409. end)
  410. :ok = Mix.Tasks.Pleroma.Relay.run(["list"])
  411. assert_receive {:mix_shell, :info, ["https://relay.mastodon.host/actor"]}
  412. end
  413. @tag capture_log: true
  414. test "without valid signature, " <>
  415. "it only accepts Create activities and requires enabled federation",
  416. %{conn: conn} do
  417. data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
  418. non_create_data = File.read!("test/fixtures/mastodon-announce.json") |> Jason.decode!()
  419. conn = put_req_header(conn, "content-type", "application/activity+json")
  420. clear_config([:instance, :federating], false)
  421. conn
  422. |> post("/inbox", data)
  423. |> json_response(403)
  424. conn
  425. |> post("/inbox", non_create_data)
  426. |> json_response(403)
  427. clear_config([:instance, :federating], true)
  428. ret_conn = post(conn, "/inbox", data)
  429. assert "ok" == json_response(ret_conn, 200)
  430. conn
  431. |> post("/inbox", non_create_data)
  432. |> json_response(400)
  433. end
  434. end
  435. describe "/users/:nickname/inbox" do
  436. setup do
  437. data =
  438. File.read!("test/fixtures/mastodon-post-activity.json")
  439. |> Jason.decode!()
  440. [data: data]
  441. end
  442. test "it inserts an incoming activity into the database", %{conn: conn, data: data} do
  443. user = insert(:user)
  444. data = Map.put(data, "bcc", [user.ap_id])
  445. conn =
  446. conn
  447. |> assign(:valid_signature, true)
  448. |> put_req_header("content-type", "application/activity+json")
  449. |> post("/users/#{user.nickname}/inbox", data)
  450. assert "ok" == json_response(conn, 200)
  451. ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
  452. assert Activity.get_by_ap_id(data["id"])
  453. end
  454. test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do
  455. user = insert(:user)
  456. data =
  457. Map.put(data, "to", user.ap_id)
  458. |> Map.delete("cc")
  459. conn =
  460. conn
  461. |> assign(:valid_signature, true)
  462. |> put_req_header("content-type", "application/activity+json")
  463. |> post("/users/#{user.nickname}/inbox", data)
  464. assert "ok" == json_response(conn, 200)
  465. ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
  466. assert Activity.get_by_ap_id(data["id"])
  467. end
  468. test "it accepts messages with cc as string instead of array", %{conn: conn, data: data} do
  469. user = insert(:user)
  470. data =
  471. Map.put(data, "cc", user.ap_id)
  472. |> Map.delete("to")
  473. conn =
  474. conn
  475. |> assign(:valid_signature, true)
  476. |> put_req_header("content-type", "application/activity+json")
  477. |> post("/users/#{user.nickname}/inbox", data)
  478. assert "ok" == json_response(conn, 200)
  479. ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
  480. %Activity{} = activity = Activity.get_by_ap_id(data["id"])
  481. assert user.ap_id in activity.recipients
  482. end
  483. test "it accepts messages with bcc as string instead of array", %{conn: conn, data: data} do
  484. user = insert(:user)
  485. data =
  486. Map.put(data, "bcc", user.ap_id)
  487. |> Map.delete("to")
  488. |> Map.delete("cc")
  489. conn =
  490. conn
  491. |> assign(:valid_signature, true)
  492. |> put_req_header("content-type", "application/activity+json")
  493. |> post("/users/#{user.nickname}/inbox", data)
  494. assert "ok" == json_response(conn, 200)
  495. ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
  496. assert Activity.get_by_ap_id(data["id"])
  497. end
  498. test "it accepts announces with to as string instead of array", %{conn: conn} do
  499. user = insert(:user)
  500. {:ok, post} = CommonAPI.post(user, %{status: "hey"})
  501. announcer = insert(:user, local: false)
  502. data = %{
  503. "@context" => "https://www.w3.org/ns/activitystreams",
  504. "actor" => announcer.ap_id,
  505. "id" => "#{announcer.ap_id}/statuses/19512778738411822/activity",
  506. "object" => post.data["object"],
  507. "to" => "https://www.w3.org/ns/activitystreams#Public",
  508. "cc" => [user.ap_id],
  509. "type" => "Announce"
  510. }
  511. conn =
  512. conn
  513. |> assign(:valid_signature, true)
  514. |> put_req_header("content-type", "application/activity+json")
  515. |> post("/users/#{user.nickname}/inbox", data)
  516. assert "ok" == json_response(conn, 200)
  517. ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
  518. %Activity{} = activity = Activity.get_by_ap_id(data["id"])
  519. assert "https://www.w3.org/ns/activitystreams#Public" in activity.recipients
  520. end
  521. test "it accepts messages from actors that are followed by the user", %{
  522. conn: conn,
  523. data: data
  524. } do
  525. recipient = insert(:user)
  526. actor = insert(:user, %{ap_id: "http://mastodon.example.org/users/actor"})
  527. {:ok, recipient, actor} = User.follow(recipient, actor)
  528. object =
  529. data["object"]
  530. |> Map.put("attributedTo", actor.ap_id)
  531. data =
  532. data
  533. |> Map.put("actor", actor.ap_id)
  534. |> Map.put("object", object)
  535. conn =
  536. conn
  537. |> assign(:valid_signature, true)
  538. |> put_req_header("content-type", "application/activity+json")
  539. |> post("/users/#{recipient.nickname}/inbox", data)
  540. assert "ok" == json_response(conn, 200)
  541. ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
  542. assert Activity.get_by_ap_id(data["id"])
  543. end
  544. test "it rejects reads from other users", %{conn: conn} do
  545. user = insert(:user)
  546. other_user = insert(:user)
  547. conn =
  548. conn
  549. |> assign(:user, other_user)
  550. |> put_req_header("accept", "application/activity+json")
  551. |> get("/users/#{user.nickname}/inbox")
  552. assert json_response(conn, 403)
  553. end
  554. test "it returns a note activity in a collection", %{conn: conn} do
  555. note_activity = insert(:direct_note_activity)
  556. note_object = Object.normalize(note_activity, fetch: false)
  557. user = User.get_cached_by_ap_id(hd(note_activity.data["to"]))
  558. conn =
  559. conn
  560. |> assign(:user, user)
  561. |> put_req_header("accept", "application/activity+json")
  562. |> get("/users/#{user.nickname}/inbox?page=true")
  563. assert response(conn, 200) =~ note_object.data["content"]
  564. end
  565. test "it clears `unreachable` federation status of the sender", %{conn: conn, data: data} do
  566. user = insert(:user)
  567. data = Map.put(data, "bcc", [user.ap_id])
  568. sender_host = URI.parse(data["actor"]).host
  569. Instances.set_consistently_unreachable(sender_host)
  570. refute Instances.reachable?(sender_host)
  571. conn =
  572. conn
  573. |> assign(:valid_signature, true)
  574. |> put_req_header("content-type", "application/activity+json")
  575. |> post("/users/#{user.nickname}/inbox", data)
  576. assert "ok" == json_response(conn, 200)
  577. assert Instances.reachable?(sender_host)
  578. end
  579. test "it removes all follower collections but actor's", %{conn: conn} do
  580. [actor, recipient] = insert_pair(:user)
  581. data =
  582. File.read!("test/fixtures/activitypub-client-post-activity.json")
  583. |> Jason.decode!()
  584. object = Map.put(data["object"], "attributedTo", actor.ap_id)
  585. data =
  586. data
  587. |> Map.put("id", Utils.generate_object_id())
  588. |> Map.put("actor", actor.ap_id)
  589. |> Map.put("object", object)
  590. |> Map.put("cc", [
  591. recipient.follower_address,
  592. actor.follower_address
  593. ])
  594. |> Map.put("to", [
  595. recipient.ap_id,
  596. recipient.follower_address,
  597. "https://www.w3.org/ns/activitystreams#Public"
  598. ])
  599. conn
  600. |> assign(:valid_signature, true)
  601. |> put_req_header("content-type", "application/activity+json")
  602. |> post("/users/#{recipient.nickname}/inbox", data)
  603. |> json_response(200)
  604. ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
  605. activity = Activity.get_by_ap_id(data["id"])
  606. assert activity.id
  607. assert actor.follower_address in activity.recipients
  608. assert actor.follower_address in activity.data["cc"]
  609. refute recipient.follower_address in activity.recipients
  610. refute recipient.follower_address in activity.data["cc"]
  611. refute recipient.follower_address in activity.data["to"]
  612. end
  613. test "it requires authentication", %{conn: conn} do
  614. user = insert(:user)
  615. conn = put_req_header(conn, "accept", "application/activity+json")
  616. ret_conn = get(conn, "/users/#{user.nickname}/inbox")
  617. assert json_response(ret_conn, 403)
  618. ret_conn =
  619. conn
  620. |> assign(:user, user)
  621. |> get("/users/#{user.nickname}/inbox")
  622. assert json_response(ret_conn, 200)
  623. end
  624. @tag capture_log: true
  625. test "forwarded report", %{conn: conn} do
  626. admin = insert(:user, is_admin: true)
  627. actor = insert(:user, local: false)
  628. remote_domain = URI.parse(actor.ap_id).host
  629. reported_user = insert(:user)
  630. note = insert(:note_activity, user: reported_user)
  631. data = %{
  632. "@context" => [
  633. "https://www.w3.org/ns/activitystreams",
  634. "https://#{remote_domain}/schemas/litepub-0.1.jsonld",
  635. %{
  636. "@language" => "und"
  637. }
  638. ],
  639. "actor" => actor.ap_id,
  640. "cc" => [
  641. reported_user.ap_id
  642. ],
  643. "content" => "test",
  644. "context" => "context",
  645. "id" => "http://#{remote_domain}/activities/02be56cf-35e3-46b4-b2c6-47ae08dfee9e",
  646. "nickname" => reported_user.nickname,
  647. "object" => [
  648. reported_user.ap_id,
  649. %{
  650. "actor" => %{
  651. "actor_type" => "Person",
  652. "approval_pending" => false,
  653. "avatar" => "",
  654. "confirmation_pending" => false,
  655. "deactivated" => false,
  656. "display_name" => "test user",
  657. "id" => reported_user.id,
  658. "local" => false,
  659. "nickname" => reported_user.nickname,
  660. "registration_reason" => nil,
  661. "roles" => %{
  662. "admin" => false,
  663. "moderator" => false
  664. },
  665. "tags" => [],
  666. "url" => reported_user.ap_id
  667. },
  668. "content" => "",
  669. "id" => note.data["id"],
  670. "published" => note.data["published"],
  671. "type" => "Note"
  672. }
  673. ],
  674. "published" => note.data["published"],
  675. "state" => "open",
  676. "to" => [],
  677. "type" => "Flag"
  678. }
  679. conn
  680. |> assign(:valid_signature, true)
  681. |> put_req_header("content-type", "application/activity+json")
  682. |> post("/users/#{reported_user.nickname}/inbox", data)
  683. |> json_response(200)
  684. ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
  685. assert Pleroma.Repo.aggregate(Activity, :count, :id) == 2
  686. ObanHelpers.perform_all()
  687. Swoosh.TestAssertions.assert_email_sent(
  688. to: {admin.name, admin.email},
  689. html_body: ~r/Reported Account:/i
  690. )
  691. end
  692. @tag capture_log: true
  693. test "forwarded report from mastodon", %{conn: conn} do
  694. admin = insert(:user, is_admin: true)
  695. actor = insert(:user, local: false)
  696. remote_domain = URI.parse(actor.ap_id).host
  697. remote_actor = "https://#{remote_domain}/actor"
  698. [reported_user, another] = insert_list(2, :user)
  699. note = insert(:note_activity, user: reported_user)
  700. Pleroma.Web.CommonAPI.favorite(another, note.id)
  701. mock_json_body =
  702. "test/fixtures/mastodon/application_actor.json"
  703. |> File.read!()
  704. |> String.replace("{{DOMAIN}}", remote_domain)
  705. Tesla.Mock.mock(fn %{url: ^remote_actor} ->
  706. %Tesla.Env{
  707. status: 200,
  708. body: mock_json_body,
  709. headers: [{"content-type", "application/activity+json"}]
  710. }
  711. end)
  712. data = %{
  713. "@context" => "https://www.w3.org/ns/activitystreams",
  714. "actor" => remote_actor,
  715. "content" => "test report",
  716. "id" => "https://#{remote_domain}/e3b12fd1-948c-446e-b93b-a5e67edbe1d8",
  717. "nickname" => reported_user.nickname,
  718. "object" => [
  719. reported_user.ap_id,
  720. note.data["object"]
  721. ],
  722. "type" => "Flag"
  723. }
  724. conn
  725. |> assign(:valid_signature, true)
  726. |> put_req_header("content-type", "application/activity+json")
  727. |> post("/users/#{reported_user.nickname}/inbox", data)
  728. |> json_response(200)
  729. ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
  730. flag_activity = "Flag" |> Pleroma.Activity.Queries.by_type() |> Pleroma.Repo.one()
  731. reported_user_ap_id = reported_user.ap_id
  732. [^reported_user_ap_id, flag_data] = flag_activity.data["object"]
  733. Enum.each(~w(actor content id published type), &Map.has_key?(flag_data, &1))
  734. ObanHelpers.perform_all()
  735. Swoosh.TestAssertions.assert_email_sent(
  736. to: {admin.name, admin.email},
  737. html_body: ~r/#{note.data["object"]}/i
  738. )
  739. end
  740. end
  741. describe "GET /users/:nickname/outbox" do
  742. test "it paginates correctly", %{conn: conn} do
  743. user = insert(:user)
  744. conn = assign(conn, :user, user)
  745. outbox_endpoint = user.ap_id <> "/outbox"
  746. _posts =
  747. for i <- 0..25 do
  748. {:ok, activity} = CommonAPI.post(user, %{status: "post #{i}"})
  749. activity
  750. end
  751. result =
  752. conn
  753. |> put_req_header("accept", "application/activity+json")
  754. |> get(outbox_endpoint <> "?page=true")
  755. |> json_response(200)
  756. result_ids = Enum.map(result["orderedItems"], fn x -> x["id"] end)
  757. assert length(result["orderedItems"]) == 20
  758. assert length(result_ids) == 20
  759. assert result["next"]
  760. assert String.starts_with?(result["next"], outbox_endpoint)
  761. result_next =
  762. conn
  763. |> put_req_header("accept", "application/activity+json")
  764. |> get(result["next"])
  765. |> json_response(200)
  766. result_next_ids = Enum.map(result_next["orderedItems"], fn x -> x["id"] end)
  767. assert length(result_next["orderedItems"]) == 6
  768. assert length(result_next_ids) == 6
  769. refute Enum.find(result_next_ids, fn x -> x in result_ids end)
  770. refute Enum.find(result_ids, fn x -> x in result_next_ids end)
  771. assert String.starts_with?(result["id"], outbox_endpoint)
  772. result_next_again =
  773. conn
  774. |> put_req_header("accept", "application/activity+json")
  775. |> get(result_next["id"])
  776. |> json_response(200)
  777. assert result_next == result_next_again
  778. end
  779. test "it returns 200 even if there're no activities", %{conn: conn} do
  780. user = insert(:user)
  781. outbox_endpoint = user.ap_id <> "/outbox"
  782. conn =
  783. conn
  784. |> assign(:user, user)
  785. |> put_req_header("accept", "application/activity+json")
  786. |> get(outbox_endpoint)
  787. result = json_response(conn, 200)
  788. assert outbox_endpoint == result["id"]
  789. end
  790. test "it returns a note activity in a collection", %{conn: conn} do
  791. note_activity = insert(:note_activity)
  792. note_object = Object.normalize(note_activity, fetch: false)
  793. user = User.get_cached_by_ap_id(note_activity.data["actor"])
  794. conn =
  795. conn
  796. |> assign(:user, user)
  797. |> put_req_header("accept", "application/activity+json")
  798. |> get("/users/#{user.nickname}/outbox?page=true")
  799. assert response(conn, 200) =~ note_object.data["content"]
  800. end
  801. test "it returns an announce activity in a collection", %{conn: conn} do
  802. announce_activity = insert(:announce_activity)
  803. user = User.get_cached_by_ap_id(announce_activity.data["actor"])
  804. conn =
  805. conn
  806. |> assign(:user, user)
  807. |> put_req_header("accept", "application/activity+json")
  808. |> get("/users/#{user.nickname}/outbox?page=true")
  809. assert response(conn, 200) =~ announce_activity.data["object"]
  810. end
  811. end
  812. describe "POST /users/:nickname/outbox (C2S)" do
  813. setup do: clear_config([:instance, :limit])
  814. setup do
  815. [
  816. activity: %{
  817. "@context" => "https://www.w3.org/ns/activitystreams",
  818. "type" => "Create",
  819. "object" => %{"type" => "Note", "content" => "AP C2S test"},
  820. "to" => "https://www.w3.org/ns/activitystreams#Public",
  821. "cc" => []
  822. }
  823. ]
  824. end
  825. test "it rejects posts from other users / unauthenticated users", %{
  826. conn: conn,
  827. activity: activity
  828. } do
  829. user = insert(:user)
  830. other_user = insert(:user)
  831. conn = put_req_header(conn, "content-type", "application/activity+json")
  832. conn
  833. |> post("/users/#{user.nickname}/outbox", activity)
  834. |> json_response(403)
  835. conn
  836. |> assign(:user, other_user)
  837. |> post("/users/#{user.nickname}/outbox", activity)
  838. |> json_response(403)
  839. end
  840. test "it inserts an incoming create activity into the database", %{
  841. conn: conn,
  842. activity: activity
  843. } do
  844. user = insert(:user)
  845. result =
  846. conn
  847. |> assign(:user, user)
  848. |> put_req_header("content-type", "application/activity+json")
  849. |> post("/users/#{user.nickname}/outbox", activity)
  850. |> json_response(201)
  851. assert Activity.get_by_ap_id(result["id"])
  852. assert result["object"]
  853. assert %Object{data: object} = Object.normalize(result["object"], fetch: false)
  854. assert object["content"] == activity["object"]["content"]
  855. end
  856. test "it rejects anything beyond 'Note' creations", %{conn: conn, activity: activity} do
  857. user = insert(:user)
  858. activity =
  859. activity
  860. |> put_in(["object", "type"], "Benis")
  861. _result =
  862. conn
  863. |> assign(:user, user)
  864. |> put_req_header("content-type", "application/activity+json")
  865. |> post("/users/#{user.nickname}/outbox", activity)
  866. |> json_response(400)
  867. end
  868. test "it inserts an incoming sensitive activity into the database", %{
  869. conn: conn,
  870. activity: activity
  871. } do
  872. user = insert(:user)
  873. conn = assign(conn, :user, user)
  874. object = Map.put(activity["object"], "sensitive", true)
  875. activity = Map.put(activity, "object", object)
  876. response =
  877. conn
  878. |> put_req_header("content-type", "application/activity+json")
  879. |> post("/users/#{user.nickname}/outbox", activity)
  880. |> json_response(201)
  881. assert Activity.get_by_ap_id(response["id"])
  882. assert response["object"]
  883. assert %Object{data: response_object} = Object.normalize(response["object"], fetch: false)
  884. assert response_object["sensitive"] == true
  885. assert response_object["content"] == activity["object"]["content"]
  886. representation =
  887. conn
  888. |> put_req_header("accept", "application/activity+json")
  889. |> get(response["id"])
  890. |> json_response(200)
  891. assert representation["object"]["sensitive"] == true
  892. end
  893. test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do
  894. user = insert(:user)
  895. activity = Map.put(activity, "type", "BadType")
  896. conn =
  897. conn
  898. |> assign(:user, user)
  899. |> put_req_header("content-type", "application/activity+json")
  900. |> post("/users/#{user.nickname}/outbox", activity)
  901. assert json_response(conn, 400)
  902. end
  903. test "it erects a tombstone when receiving a delete activity", %{conn: conn} do
  904. note_activity = insert(:note_activity)
  905. note_object = Object.normalize(note_activity, fetch: false)
  906. user = User.get_cached_by_ap_id(note_activity.data["actor"])
  907. data = %{
  908. type: "Delete",
  909. object: %{
  910. id: note_object.data["id"]
  911. }
  912. }
  913. conn =
  914. conn
  915. |> assign(:user, user)
  916. |> put_req_header("content-type", "application/activity+json")
  917. |> post("/users/#{user.nickname}/outbox", data)
  918. result = json_response(conn, 201)
  919. assert Activity.get_by_ap_id(result["id"])
  920. assert object = Object.get_by_ap_id(note_object.data["id"])
  921. assert object.data["type"] == "Tombstone"
  922. end
  923. test "it rejects delete activity of object from other actor", %{conn: conn} do
  924. note_activity = insert(:note_activity)
  925. note_object = Object.normalize(note_activity, fetch: false)
  926. user = insert(:user)
  927. data = %{
  928. type: "Delete",
  929. object: %{
  930. id: note_object.data["id"]
  931. }
  932. }
  933. conn =
  934. conn
  935. |> assign(:user, user)
  936. |> put_req_header("content-type", "application/activity+json")
  937. |> post("/users/#{user.nickname}/outbox", data)
  938. assert json_response(conn, 400)
  939. end
  940. test "it increases like count when receiving a like action", %{conn: conn} do
  941. note_activity = insert(:note_activity)
  942. note_object = Object.normalize(note_activity, fetch: false)
  943. user = User.get_cached_by_ap_id(note_activity.data["actor"])
  944. data = %{
  945. type: "Like",
  946. object: %{
  947. id: note_object.data["id"]
  948. }
  949. }
  950. conn =
  951. conn
  952. |> assign(:user, user)
  953. |> put_req_header("content-type", "application/activity+json")
  954. |> post("/users/#{user.nickname}/outbox", data)
  955. result = json_response(conn, 201)
  956. assert Activity.get_by_ap_id(result["id"])
  957. assert object = Object.get_by_ap_id(note_object.data["id"])
  958. assert object.data["like_count"] == 1
  959. end
  960. test "it doesn't spreads faulty attributedTo or actor fields", %{
  961. conn: conn,
  962. activity: activity
  963. } do
  964. reimu = insert(:user, nickname: "reimu")
  965. cirno = insert(:user, nickname: "cirno")
  966. assert reimu.ap_id
  967. assert cirno.ap_id
  968. activity =
  969. activity
  970. |> put_in(["object", "actor"], reimu.ap_id)
  971. |> put_in(["object", "attributedTo"], reimu.ap_id)
  972. |> put_in(["actor"], reimu.ap_id)
  973. |> put_in(["attributedTo"], reimu.ap_id)
  974. _reimu_outbox =
  975. conn
  976. |> assign(:user, cirno)
  977. |> put_req_header("content-type", "application/activity+json")
  978. |> post("/users/#{reimu.nickname}/outbox", activity)
  979. |> json_response(403)
  980. cirno_outbox =
  981. conn
  982. |> assign(:user, cirno)
  983. |> put_req_header("content-type", "application/activity+json")
  984. |> post("/users/#{cirno.nickname}/outbox", activity)
  985. |> json_response(201)
  986. assert cirno_outbox["attributedTo"] == nil
  987. assert cirno_outbox["actor"] == cirno.ap_id
  988. assert cirno_object = Object.normalize(cirno_outbox["object"], fetch: false)
  989. assert cirno_object.data["actor"] == cirno.ap_id
  990. assert cirno_object.data["attributedTo"] == cirno.ap_id
  991. end
  992. test "Character limitation", %{conn: conn, activity: activity} do
  993. clear_config([:instance, :limit], 5)
  994. user = insert(:user)
  995. result =
  996. conn
  997. |> assign(:user, user)
  998. |> put_req_header("content-type", "application/activity+json")
  999. |> post("/users/#{user.nickname}/outbox", activity)
  1000. |> json_response(400)
  1001. assert result == "Note is over the character limit"
  1002. end
  1003. end
  1004. describe "/relay/followers" do
  1005. test "it returns relay followers", %{conn: conn} do
  1006. relay_actor = Relay.get_actor()
  1007. user = insert(:user)
  1008. User.follow(user, relay_actor)
  1009. result =
  1010. conn
  1011. |> get("/relay/followers")
  1012. |> json_response(200)
  1013. assert result["first"]["orderedItems"] == [user.ap_id]
  1014. end
  1015. test "on non-federating instance, it returns 404", %{conn: conn} do
  1016. clear_config([:instance, :federating], false)
  1017. user = insert(:user)
  1018. conn
  1019. |> assign(:user, user)
  1020. |> get("/relay/followers")
  1021. |> json_response(404)
  1022. end
  1023. end
  1024. describe "/relay/following" do
  1025. test "it returns relay following", %{conn: conn} do
  1026. result =
  1027. conn
  1028. |> get("/relay/following")
  1029. |> json_response(200)
  1030. assert result["first"]["orderedItems"] == []
  1031. end
  1032. test "on non-federating instance, it returns 404", %{conn: conn} do
  1033. clear_config([:instance, :federating], false)
  1034. user = insert(:user)
  1035. conn
  1036. |> assign(:user, user)
  1037. |> get("/relay/following")
  1038. |> json_response(404)
  1039. end
  1040. end
  1041. describe "/users/:nickname/followers" do
  1042. test "it returns the followers in a collection", %{conn: conn} do
  1043. user = insert(:user)
  1044. user_two = insert(:user)
  1045. User.follow(user, user_two)
  1046. result =
  1047. conn
  1048. |> assign(:user, user_two)
  1049. |> get("/users/#{user_two.nickname}/followers")
  1050. |> json_response(200)
  1051. assert result["first"]["orderedItems"] == [user.ap_id]
  1052. end
  1053. test "it returns a uri if the user has 'hide_followers' set", %{conn: conn} do
  1054. user = insert(:user)
  1055. user_two = insert(:user, hide_followers: true)
  1056. User.follow(user, user_two)
  1057. result =
  1058. conn
  1059. |> assign(:user, user)
  1060. |> get("/users/#{user_two.nickname}/followers")
  1061. |> json_response(200)
  1062. assert is_binary(result["first"])
  1063. end
  1064. test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is from another user",
  1065. %{conn: conn} do
  1066. user = insert(:user)
  1067. other_user = insert(:user, hide_followers: true)
  1068. result =
  1069. conn
  1070. |> assign(:user, user)
  1071. |> get("/users/#{other_user.nickname}/followers?page=1")
  1072. assert result.status == 403
  1073. assert result.resp_body == ""
  1074. end
  1075. test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user",
  1076. %{conn: conn} do
  1077. user = insert(:user, hide_followers: true)
  1078. other_user = insert(:user)
  1079. {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
  1080. result =
  1081. conn
  1082. |> assign(:user, user)
  1083. |> get("/users/#{user.nickname}/followers?page=1")
  1084. |> json_response(200)
  1085. assert result["totalItems"] == 1
  1086. assert result["orderedItems"] == [other_user.ap_id]
  1087. end
  1088. test "it works for more than 10 users", %{conn: conn} do
  1089. user = insert(:user)
  1090. Enum.each(1..15, fn _ ->
  1091. other_user = insert(:user)
  1092. User.follow(other_user, user)
  1093. end)
  1094. result =
  1095. conn
  1096. |> assign(:user, user)
  1097. |> get("/users/#{user.nickname}/followers")
  1098. |> json_response(200)
  1099. assert length(result["first"]["orderedItems"]) == 10
  1100. assert result["first"]["totalItems"] == 15
  1101. assert result["totalItems"] == 15
  1102. result =
  1103. conn
  1104. |> assign(:user, user)
  1105. |> get("/users/#{user.nickname}/followers?page=2")
  1106. |> json_response(200)
  1107. assert length(result["orderedItems"]) == 5
  1108. assert result["totalItems"] == 15
  1109. end
  1110. test "does not require authentication", %{conn: conn} do
  1111. user = insert(:user)
  1112. conn
  1113. |> get("/users/#{user.nickname}/followers")
  1114. |> json_response(200)
  1115. end
  1116. end
  1117. describe "/users/:nickname/following" do
  1118. test "it returns the following in a collection", %{conn: conn} do
  1119. user = insert(:user)
  1120. user_two = insert(:user)
  1121. User.follow(user, user_two)
  1122. result =
  1123. conn
  1124. |> assign(:user, user)
  1125. |> get("/users/#{user.nickname}/following")
  1126. |> json_response(200)
  1127. assert result["first"]["orderedItems"] == [user_two.ap_id]
  1128. end
  1129. test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do
  1130. user = insert(:user)
  1131. user_two = insert(:user, hide_follows: true)
  1132. User.follow(user, user_two)
  1133. result =
  1134. conn
  1135. |> assign(:user, user)
  1136. |> get("/users/#{user_two.nickname}/following")
  1137. |> json_response(200)
  1138. assert is_binary(result["first"])
  1139. end
  1140. test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is from another user",
  1141. %{conn: conn} do
  1142. user = insert(:user)
  1143. user_two = insert(:user, hide_follows: true)
  1144. result =
  1145. conn
  1146. |> assign(:user, user)
  1147. |> get("/users/#{user_two.nickname}/following?page=1")
  1148. assert result.status == 403
  1149. assert result.resp_body == ""
  1150. end
  1151. test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user",
  1152. %{conn: conn} do
  1153. user = insert(:user, hide_follows: true)
  1154. other_user = insert(:user)
  1155. {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
  1156. result =
  1157. conn
  1158. |> assign(:user, user)
  1159. |> get("/users/#{user.nickname}/following?page=1")
  1160. |> json_response(200)
  1161. assert result["totalItems"] == 1
  1162. assert result["orderedItems"] == [other_user.ap_id]
  1163. end
  1164. test "it works for more than 10 users", %{conn: conn} do
  1165. user = insert(:user)
  1166. Enum.each(1..15, fn _ ->
  1167. user = User.get_cached_by_id(user.id)
  1168. other_user = insert(:user)
  1169. User.follow(user, other_user)
  1170. end)
  1171. result =
  1172. conn
  1173. |> assign(:user, user)
  1174. |> get("/users/#{user.nickname}/following")
  1175. |> json_response(200)
  1176. assert length(result["first"]["orderedItems"]) == 10
  1177. assert result["first"]["totalItems"] == 15
  1178. assert result["totalItems"] == 15
  1179. result =
  1180. conn
  1181. |> assign(:user, user)
  1182. |> get("/users/#{user.nickname}/following?page=2")
  1183. |> json_response(200)
  1184. assert length(result["orderedItems"]) == 5
  1185. assert result["totalItems"] == 15
  1186. end
  1187. test "does not require authentication", %{conn: conn} do
  1188. user = insert(:user)
  1189. conn
  1190. |> get("/users/#{user.nickname}/following")
  1191. |> json_response(200)
  1192. end
  1193. end
  1194. describe "delivery tracking" do
  1195. test "it tracks a signed object fetch", %{conn: conn} do
  1196. user = insert(:user, local: false)
  1197. activity = insert(:note_activity)
  1198. object = Object.normalize(activity, fetch: false)
  1199. object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
  1200. conn
  1201. |> put_req_header("accept", "application/activity+json")
  1202. |> assign(:user, user)
  1203. |> get(object_path)
  1204. |> json_response(200)
  1205. assert Delivery.get(object.id, user.id)
  1206. end
  1207. test "it tracks a signed activity fetch", %{conn: conn} do
  1208. user = insert(:user, local: false)
  1209. activity = insert(:note_activity)
  1210. object = Object.normalize(activity, fetch: false)
  1211. activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
  1212. conn
  1213. |> put_req_header("accept", "application/activity+json")
  1214. |> assign(:user, user)
  1215. |> get(activity_path)
  1216. |> json_response(200)
  1217. assert Delivery.get(object.id, user.id)
  1218. end
  1219. test "it tracks a signed object fetch when the json is cached", %{conn: conn} do
  1220. user = insert(:user, local: false)
  1221. other_user = insert(:user, local: false)
  1222. activity = insert(:note_activity)
  1223. object = Object.normalize(activity, fetch: false)
  1224. object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
  1225. conn
  1226. |> put_req_header("accept", "application/activity+json")
  1227. |> assign(:user, user)
  1228. |> get(object_path)
  1229. |> json_response(200)
  1230. build_conn()
  1231. |> put_req_header("accept", "application/activity+json")
  1232. |> assign(:user, other_user)
  1233. |> get(object_path)
  1234. |> json_response(200)
  1235. assert Delivery.get(object.id, user.id)
  1236. assert Delivery.get(object.id, other_user.id)
  1237. end
  1238. test "it tracks a signed activity fetch when the json is cached", %{conn: conn} do
  1239. user = insert(:user, local: false)
  1240. other_user = insert(:user, local: false)
  1241. activity = insert(:note_activity)
  1242. object = Object.normalize(activity, fetch: false)
  1243. activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
  1244. conn
  1245. |> put_req_header("accept", "application/activity+json")
  1246. |> assign(:user, user)
  1247. |> get(activity_path)
  1248. |> json_response(200)
  1249. build_conn()
  1250. |> put_req_header("accept", "application/activity+json")
  1251. |> assign(:user, other_user)
  1252. |> get(activity_path)
  1253. |> json_response(200)
  1254. assert Delivery.get(object.id, user.id)
  1255. assert Delivery.get(object.id, other_user.id)
  1256. end
  1257. end
  1258. describe "Additional ActivityPub C2S endpoints" do
  1259. test "GET /api/ap/whoami", %{conn: conn} do
  1260. user = insert(:user)
  1261. conn =
  1262. conn
  1263. |> assign(:user, user)
  1264. |> get("/api/ap/whoami")
  1265. user = User.get_cached_by_id(user.id)
  1266. assert UserView.render("user.json", %{user: user}) == json_response(conn, 200)
  1267. conn
  1268. |> get("/api/ap/whoami")
  1269. |> json_response(403)
  1270. end
  1271. setup do: clear_config([:media_proxy])
  1272. setup do: clear_config([Pleroma.Upload])
  1273. test "POST /api/ap/upload_media", %{conn: conn} do
  1274. user = insert(:user)
  1275. desc = "Description of the image"
  1276. image = %Plug.Upload{
  1277. content_type: "image/jpeg",
  1278. path: Path.absname("test/fixtures/image.jpg"),
  1279. filename: "an_image.jpg"
  1280. }
  1281. object =
  1282. conn
  1283. |> assign(:user, user)
  1284. |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
  1285. |> json_response(:created)
  1286. assert object["name"] == desc
  1287. assert object["type"] == "Document"
  1288. assert object["actor"] == user.ap_id
  1289. assert [%{"href" => object_href, "mediaType" => object_mediatype}] = object["url"]
  1290. assert is_binary(object_href)
  1291. assert object_mediatype == "image/jpeg"
  1292. assert String.ends_with?(object_href, ".jpg")
  1293. activity_request = %{
  1294. "@context" => "https://www.w3.org/ns/activitystreams",
  1295. "type" => "Create",
  1296. "object" => %{
  1297. "type" => "Note",
  1298. "content" => "AP C2S test, attachment",
  1299. "attachment" => [object]
  1300. },
  1301. "to" => "https://www.w3.org/ns/activitystreams#Public",
  1302. "cc" => []
  1303. }
  1304. activity_response =
  1305. conn
  1306. |> assign(:user, user)
  1307. |> post("/users/#{user.nickname}/outbox", activity_request)
  1308. |> json_response(:created)
  1309. assert activity_response["id"]
  1310. assert activity_response["object"]
  1311. assert activity_response["actor"] == user.ap_id
  1312. assert %Object{data: %{"attachment" => [attachment]}} =
  1313. Object.normalize(activity_response["object"], fetch: false)
  1314. assert attachment["type"] == "Document"
  1315. assert attachment["name"] == desc
  1316. assert [
  1317. %{
  1318. "href" => ^object_href,
  1319. "type" => "Link",
  1320. "mediaType" => ^object_mediatype
  1321. }
  1322. ] = attachment["url"]
  1323. # Fails if unauthenticated
  1324. conn
  1325. |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
  1326. |> json_response(403)
  1327. end
  1328. end
  1329. end