- Fix posts update federation and add tests - Fix posts deletion federation and add tests Signed-off-by: Thomas Citharel <tcit@tcit.fr>master
parent
0c4a7e0216
commit
fc1d392211
@ -0,0 +1,212 @@
|
||||
defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.DeleteTest do
|
||||
use Mobilizon.DataCase
|
||||
use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
|
||||
use Oban.Testing, repo: Mobilizon.Storage.Repo
|
||||
import Mobilizon.Factory
|
||||
import ExUnit.CaptureLog
|
||||
import Mox
|
||||
|
||||
alias Mobilizon.{Actors, Discussions, Events, Posts}
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Discussions.Comment
|
||||
alias Mobilizon.Events.Event
|
||||
alias Mobilizon.Posts.Post
|
||||
alias Mobilizon.Federation.ActivityPub.{Activity, Transmogrifier}
|
||||
alias Mobilizon.Federation.ActivityStream.Convertible
|
||||
alias Mobilizon.Service.HTTP.ActivityPub.Mock
|
||||
|
||||
describe "handle incoming delete activities" do
|
||||
test "it works for incoming deletes" do
|
||||
%Actor{url: actor_url} =
|
||||
actor = insert(:actor, url: "http://mobilizon.tld/@remote", domain: "mobilizon.tld")
|
||||
|
||||
%Comment{url: comment_url} =
|
||||
insert(:comment,
|
||||
actor: nil,
|
||||
actor_id: actor.id,
|
||||
url: "http://mobilizon.tld/comments/9f3794b8-11a0-4a98-8cb7-475ab332c701"
|
||||
)
|
||||
|
||||
Mock
|
||||
|> expect(:call, fn
|
||||
%{method: :get, url: "http://mobilizon.tld/comments/9f3794b8-11a0-4a98-8cb7-475ab332c701"},
|
||||
_opts ->
|
||||
{:ok, %Tesla.Env{status: 410, body: "Gone"}}
|
||||
end)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-delete.json")
|
||||
|> Jason.decode!()
|
||||
|
||||
object =
|
||||
data["object"]
|
||||
|> Map.put("id", comment_url)
|
||||
|
||||
data =
|
||||
data
|
||||
|> Map.put("object", object)
|
||||
|> Map.put("actor", actor_url)
|
||||
|
||||
assert Discussions.get_comment_from_url(comment_url)
|
||||
assert is_nil(Discussions.get_comment_from_url(comment_url).deleted_at)
|
||||
|
||||
{:ok, %Activity{local: false}, _} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
refute is_nil(Discussions.get_comment_from_url(comment_url).deleted_at)
|
||||
end
|
||||
|
||||
test "it fails for incoming deletes with spoofed origin" do
|
||||
comment = insert(:comment)
|
||||
|
||||
announce_data =
|
||||
File.read!("test/fixtures/mastodon-announce.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("object", comment.url)
|
||||
|
||||
{:ok, _, _} = Transmogrifier.handle_incoming(announce_data)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-delete.json")
|
||||
|> Jason.decode!()
|
||||
|
||||
object =
|
||||
data["object"]
|
||||
|> Map.put("id", comment.url)
|
||||
|
||||
data =
|
||||
data
|
||||
|> Map.put("object", object)
|
||||
|
||||
:error = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert Discussions.get_comment_from_url(comment.url)
|
||||
end
|
||||
|
||||
setup :set_mox_from_context
|
||||
|
||||
test "it works for incoming actor deletes" do
|
||||
%Actor{url: url} =
|
||||
actor = insert(:actor, url: "https://framapiaf.org/users/admin", domain: "framapiaf.org")
|
||||
|
||||
%Event{url: event1_url} = event1 = insert(:event, organizer_actor: actor)
|
||||
insert(:event, organizer_actor: actor)
|
||||
|
||||
%Comment{url: comment1_url} = comment1 = insert(:comment, actor: actor)
|
||||
insert(:comment, actor: actor)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-delete-user.json")
|
||||
|> Jason.decode!()
|
||||
|
||||
Mock
|
||||
|> expect(:call, fn
|
||||
%{method: :get, url: "https://framapiaf.org/users/admin"}, _opts ->
|
||||
{:ok, %Tesla.Env{status: 410, body: "Gone"}}
|
||||
end)
|
||||
|
||||
{:ok, _activity, _actor} = Transmogrifier.handle_incoming(data)
|
||||
assert %{success: 1, failure: 0} == Oban.drain_queue(queue: :background)
|
||||
|
||||
assert {:error, :actor_not_found} = Actors.get_actor_by_url(url)
|
||||
assert {:error, :event_not_found} = Events.get_event(event1.id)
|
||||
# Tombstone are cascade deleted, seems correct for now
|
||||
# assert %Tombstone{} = Tombstone.find_tombstone(event1_url)
|
||||
assert %Comment{deleted_at: deleted_at} = Discussions.get_comment(comment1.id)
|
||||
refute is_nil(deleted_at)
|
||||
# assert %Tombstone{} = Tombstone.find_tombstone(comment1_url)
|
||||
end
|
||||
|
||||
test "it fails for incoming actor deletes with spoofed origin" do
|
||||
%{url: url} = insert(:actor)
|
||||
deleted_actor_url = "https://framapiaf.org/users/admin"
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-delete-user.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("actor", url)
|
||||
|
||||
deleted_actor_data =
|
||||
File.read!("test/fixtures/mastodon-actor.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("id", deleted_actor_url)
|
||||
|
||||
Mock
|
||||
|> expect(:call, fn
|
||||
%{url: ^deleted_actor_url}, _opts ->
|
||||
{:ok, %Tesla.Env{status: 200, body: deleted_actor_data}}
|
||||
end)
|
||||
|
||||
assert capture_log(fn ->
|
||||
assert :error == Transmogrifier.handle_incoming(data)
|
||||
end) =~ "Object origin check failed"
|
||||
|
||||
assert Actors.get_actor_by_url(url)
|
||||
end
|
||||
end
|
||||
|
||||
describe "handle incoming delete activities for group posts" do
|
||||
test "works for remote deletions" do
|
||||
%Actor{url: remote_actor_url} =
|
||||
insert(:actor,
|
||||
domain: "remote.domain",
|
||||
url: "https://remote.domain/@remote",
|
||||
preferred_username: "remote"
|
||||
)
|
||||
|
||||
group = insert(:group)
|
||||
%Post{} = post = insert(:post, attributed_to: group)
|
||||
|
||||
data = Convertible.model_to_as(post)
|
||||
refute is_nil(Posts.get_post_by_url(data["id"]))
|
||||
|
||||
delete_data =
|
||||
File.read!("test/fixtures/mastodon-delete.json")
|
||||
|> Jason.decode!()
|
||||
|
||||
object =
|
||||
data
|
||||
|> Map.put("type", "Article")
|
||||
|
||||
delete_data =
|
||||
delete_data
|
||||
|> Map.put("actor", remote_actor_url)
|
||||
|> Map.put("object", object)
|
||||
|
||||
:error = Transmogrifier.handle_incoming(delete_data)
|
||||
|
||||
refute is_nil(Posts.get_post_by_url(data["id"]))
|
||||
end
|
||||
|
||||
test "doesn't work for remote deletions if the actor is not a group member" do
|
||||
%Actor{url: remote_actor_url} =
|
||||
insert(:actor,
|
||||
domain: "remote.domain",
|
||||
url: "https://remote.domain/@remote",
|
||||
preferred_username: "remote"
|
||||
)
|
||||
|
||||
group = insert(:group)
|
||||
%Post{} = post = insert(:post, attributed_to: group)
|
||||
|
||||
data = Convertible.model_to_as(post)
|
||||
refute is_nil(Posts.get_post_by_url(data["id"]))
|
||||
|
||||
delete_data =
|
||||
File.read!("test/fixtures/mastodon-delete.json")
|
||||
|> Jason.decode!()
|
||||
|
||||
object =
|
||||
data
|
||||
|> Map.put("type", "Article")
|
||||
|
||||
delete_data =
|
||||
delete_data
|
||||
|> Map.put("actor", remote_actor_url)
|
||||
|> Map.put("object", object)
|
||||
|
||||
:error = Transmogrifier.handle_incoming(delete_data)
|
||||
|
||||
refute is_nil(Posts.get_post_by_url(data["id"]))
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,181 @@
|
||||
defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.UpdateTest do
|
||||
use Mobilizon.DataCase
|
||||
use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
|
||||
use Oban.Testing, repo: Mobilizon.Storage.Repo
|
||||
import Mobilizon.Factory
|
||||
|
||||
alias Mobilizon.{Actors, Events, Posts}
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Events.Event
|
||||
alias Mobilizon.Posts.Post
|
||||
alias Mobilizon.Federation.ActivityPub.{Activity, Transmogrifier}
|
||||
alias Mobilizon.Federation.ActivityStream.Convertible
|
||||
|
||||
describe "handle incoming update activities" do
|
||||
test "it works for incoming update activities on actors" do
|
||||
use_cassette "activity_pub/update_actor_activity" do
|
||||
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
|
||||
|
||||
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
|
||||
update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
|
||||
|
||||
object =
|
||||
update_data["object"]
|
||||
|> Map.put("actor", data["actor"])
|
||||
|> Map.put("id", data["actor"])
|
||||
|
||||
update_data =
|
||||
update_data
|
||||
|> Map.put("actor", data["actor"])
|
||||
|> Map.put("object", object)
|
||||
|
||||
{:ok, %Activity{data: _data, local: false}, _} =
|
||||
Transmogrifier.handle_incoming(update_data)
|
||||
|
||||
{:ok, %Actor{} = actor} = Actors.get_actor_by_url(update_data["actor"])
|
||||
assert actor.name == "nextsoft"
|
||||
|
||||
assert actor.summary == "<p>Some bio</p>"
|
||||
end
|
||||
end
|
||||
|
||||
test "it works for incoming update activities on events" do
|
||||
use_cassette "activity_pub/event_update_activities" do
|
||||
data = File.read!("test/fixtures/mobilizon-post-activity.json") |> Jason.decode!()
|
||||
|
||||
{:ok, %Activity{data: data, local: false}, %Event{id: event_id}} =
|
||||
Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert_enqueued(
|
||||
worker: Mobilizon.Service.Workers.BuildSearch,
|
||||
args: %{event_id: event_id, op: :insert_search_event}
|
||||
)
|
||||
|
||||
assert %{success: 1, failure: 0} == Oban.drain_queue(queue: :search)
|
||||
|
||||
update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
|
||||
|
||||
object =
|
||||
data["object"]
|
||||
|> Map.put("actor", data["actor"])
|
||||
|> Map.put("name", "My updated event")
|
||||
|> Map.put("id", data["object"]["id"])
|
||||
|> Map.put("type", "Event")
|
||||
|
||||
update_data =
|
||||
update_data
|
||||
|> Map.put("actor", data["actor"])
|
||||
|> Map.put("object", object)
|
||||
|
||||
{:ok, %Activity{data: data, local: false}, _} =
|
||||
Transmogrifier.handle_incoming(update_data)
|
||||
|
||||
%Event{} = event = Events.get_event_by_url(data["object"]["id"])
|
||||
|
||||
assert_enqueued(
|
||||
worker: Mobilizon.Service.Workers.BuildSearch,
|
||||
args: %{event_id: event_id, op: :update_search_event}
|
||||
)
|
||||
|
||||
assert %{success: 1, failure: 0} == Oban.drain_queue(queue: :search)
|
||||
|
||||
assert event.title == "My updated event"
|
||||
|
||||
assert event.description == data["object"]["content"]
|
||||
end
|
||||
end
|
||||
|
||||
test "it works for incoming update activities on group posts" do
|
||||
use_cassette "activity_pub/group_post_update_activities" do
|
||||
%Actor{url: remote_actor_url} = remote_actor = insert(:actor, domain: "remote.domain")
|
||||
group = insert(:group)
|
||||
insert(:member, actor: remote_actor, parent: group)
|
||||
%Post{} = post = insert(:post, attributed_to: group)
|
||||
|
||||
data = Convertible.model_to_as(post)
|
||||
refute is_nil(Posts.get_post_by_url(data["id"]))
|
||||
|
||||
update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
|
||||
|
||||
object =
|
||||
data
|
||||
|> Map.put("actor", remote_actor_url)
|
||||
|> Map.put("name", "My updated post")
|
||||
|> Map.put("type", "Article")
|
||||
|
||||
update_data =
|
||||
update_data
|
||||
|> Map.put("actor", remote_actor_url)
|
||||
|> Map.put("object", object)
|
||||
|
||||
{:ok, %Activity{data: data, local: false}, _} =
|
||||
Transmogrifier.handle_incoming(update_data)
|
||||
|
||||
%Post{id: updated_post_id, title: updated_post_title} =
|
||||
Posts.get_post_by_url(data["object"]["id"])
|
||||
|
||||
assert updated_post_id == post.id
|
||||
assert updated_post_title == "My updated post"
|
||||
end
|
||||
end
|
||||
|
||||
test "it fails for incoming update activities on group posts when the actor is not a member from the group" do
|
||||
use_cassette "activity_pub/group_post_update_activities" do
|
||||
%Actor{url: remote_actor_url} =
|
||||
insert(:actor,
|
||||
domain: "remote.domain",
|
||||
url: "https://remote.domain/@remote",
|
||||
preferred_username: "remote"
|
||||
)
|
||||
|
||||
group = insert(:group)
|
||||
%Post{} = post = insert(:post, attributed_to: group)
|
||||
|
||||
data = Convertible.model_to_as(post)
|
||||
refute is_nil(Posts.get_post_by_url(data["id"]))
|
||||
|
||||
update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
|
||||
|
||||
object =
|
||||
data
|
||||
|> Map.put("name", "My updated post")
|
||||
|> Map.put("type", "Article")
|
||||
|
||||
update_data =
|
||||
update_data
|
||||
|> Map.put("actor", remote_actor_url)
|
||||
|> Map.put("object", object)
|
||||
|
||||
:error = Transmogrifier.handle_incoming(update_data)
|
||||
|
||||
%Post{id: updated_post_id, title: updated_post_title} = Posts.get_post_by_url(data["id"])
|
||||
|
||||
assert updated_post_id == post.id
|
||||
refute updated_post_title == "My updated post"
|
||||
end
|
||||
end
|
||||
|
||||
# test "it works for incoming update activities which lock the account" do
|
||||
# data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
|
||||
|
||||
# {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
# update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
|
||||
|
||||
# object =
|
||||
# update_data["object"]
|
||||
# |> Map.put("actor", data["actor"])
|
||||
# |> Map.put("id", data["actor"])
|
||||
# |> Map.put("manuallyApprovesFollowers", true)
|
||||
|
||||
# update_data =
|
||||
# update_data
|
||||
# |> Map.put("actor", data["actor"])
|
||||
# |> Map.put("object", object)
|
||||
|
||||
# {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
|
||||
|
||||
# user = User.get_cached_by_ap_id(data["actor"])
|
||||
# assert user.info["locked"] == true
|
||||
# end
|
||||
end
|
||||
end
|
@ -0,0 +1,24 @@
|
||||
[
|
||||
{
|
||||
"request": {
|
||||
"body": "{\"@context\":[\"https://www.w3.org/ns/activitystreams\",\"https://litepub.social/litepub/context.jsonld\",{\"Hashtag\":\"as:Hashtag\",\"PostalAddress\":\"sc:PostalAddress\",\"address\":{\"@id\":\"sc:address\",\"@type\":\"sc:PostalAddress\"},\"addressCountry\":\"sc:addressCountry\",\"addressLocality\":\"sc:addressLocality\",\"addressRegion\":\"sc:addressRegion\",\"anonymousParticipationEnabled\":{\"@id\":\"mz:anonymousParticipationEnabled\",\"@type\":\"sc:Boolean\"},\"category\":\"sc:category\",\"commentsEnabled\":{\"@id\":\"pt:commentsEnabled\",\"@type\":\"sc:Boolean\"},\"discoverable\":\"toot:discoverable\",\"ical\":\"http://www.w3.org/2002/12/cal/ical#\",\"joinMode\":{\"@id\":\"mz:joinMode\",\"@type\":\"mz:joinModeType\"},\"joinModeType\":{\"@id\":\"mz:joinModeType\",\"@type\":\"rdfs:Class\"},\"location\":{\"@id\":\"sc:location\",\"@type\":\"sc:Place\"},\"manuallyApprovesFollowers\":\"as:manuallyApprovesFollowers\",\"maximumAttendeeCapacity\":\"sc:maximumAttendeeCapacity\",\"mz\":\"https://joinmobilizon.org/ns#\",\"participationMessage\":{\"@id\":\"mz:participationMessage\",\"@type\":\"sc:Text\"},\"postalCode\":\"sc:postalCode\",\"pt\":\"https://joinpeertube.org/ns#\",\"repliesModerationOption\":{\"@id\":\"mz:repliesModerationOption\",\"@type\":\"mz:repliesModerationOptionType\"},\"repliesModerationOptionType\":{\"@id\":\"mz:repliesModerationOptionType\",\"@type\":\"rdfs:Class\"},\"sc\":\"http://schema.org#\",\"streetAddress\":\"sc:streetAddress\",\"toot\":\"http://joinmastodon.org/ns#\",\"uuid\":\"sc:identifier\"}],\"actor\":\"http://mobilizon.test/@myGroup0\",\"cc\":[],\"id\":\"http://mobilizon.test/announces/839e0ffc-f437-48db-afba-9ce1e971e938\",\"object\":{\"actor\":\"http://mobilizon.test/@thomas0\",\"attributedTo\":\"http://mobilizon.test/@myGroup0\",\"content\":\"The <b>HTML</b>body for my Article\",\"id\":\"http://mobilizon.test/p/6a482d5f-94fc-446b-84bb-d4d386d5dd45\",\"name\":\"My updated post\",\"published\":\"2020-10-19T08:37:52Z\",\"type\":\"Article\"},\"to\":[\"http://mobilizon.test/@myGroup0/members\"],\"type\":\"Announce\"}",
|
||||
"headers": {
|
||||
"Content-Type": "application/activity+json",
|
||||
"signature": "keyId=\"http://mobilizon.test/@myGroup0#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) content-length date digest host\",signature=\"P+7rSSUeUBdX74wbvSEe4roG7yh7MfpF6s4tjv5q1kbeVKtXZRyfC1LqgVNCADZYXFqYlMvfF7DiaRQRiMznGWawM/QXK08eXiAVihYK28Pa56BfI68OUakd+FptlwfB4WJ4Jc7xi1z+iarv+EvlFxjkG5pgwL4mW49rvNnigELzypGtp2bj/2BhiBItHutvOju1MwLR1EBQFJBSZDVZZKbHTcV4KbGtbYvkWUbH8fZbe3fgctKlvO/z9kw+yBTTIEE1O18F4HiJ17nYtaaxv3/vl5RxcjYLpf+QQzkaPOsSLZs8zpIZZp3BbLtPh+OGwkyK9PBQsaI0N1ZSLQ5gaQ==\"",
|
||||
"digest": "SHA-256=EyZ+uZ/Vv2lUK8ozgOHBpnoUWUM5WQHATQb1tEMldNU=",
|
||||
"date": "Mon, 19 Oct 2020 08:37:52 GMT"
|
||||
},
|
||||
"method": "post",
|
||||
"options": [],
|
||||
"request_body": "",
|
||||
"url": "http://mobilizon.test/inbox"
|
||||
},
|
||||
"response": {
|
||||
"binary": false,
|
||||
"body": "nxdomain",
|
||||
"headers": [],
|
||||
"status_code": null,
|
||||
"type": "error"
|
||||
}
|
||||
}
|
||||
]
|
Loading…
Reference in new issue