forked from AkkomaGang/akkoma
Merge branch 'announce-validator' into 'develop'
Announce validator See merge request pleroma/pleroma!2567
This commit is contained in:
commit
7b02bfca51
33 changed files with 796 additions and 367 deletions
|
@ -356,36 +356,6 @@ def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
|||
end
|
||||
end
|
||||
|
||||
@spec announce(User.t(), Object.t(), String.t() | nil, boolean(), boolean()) ::
|
||||
{:ok, Activity.t(), Object.t()} | {:error, any()}
|
||||
def announce(
|
||||
%User{ap_id: _} = user,
|
||||
%Object{data: %{"id" => _}} = object,
|
||||
activity_id \\ nil,
|
||||
local \\ true,
|
||||
public \\ true
|
||||
) do
|
||||
with {:ok, result} <-
|
||||
Repo.transaction(fn -> do_announce(user, object, activity_id, local, public) end) do
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
defp do_announce(user, object, activity_id, local, public) do
|
||||
with true <- is_announceable?(object, user, public),
|
||||
object <- Object.get_by_id(object.id),
|
||||
announce_data <- make_announce_data(user, object, activity_id, public),
|
||||
{:ok, activity} <- insert(announce_data, local),
|
||||
{:ok, object} <- add_announce_to_object(activity, object),
|
||||
_ <- notify_and_stream(activity),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity, object}
|
||||
else
|
||||
false -> {:error, false}
|
||||
{:error, error} -> Repo.rollback(error)
|
||||
end
|
||||
end
|
||||
|
||||
@spec follow(User.t(), User.t(), String.t() | nil, boolean()) ::
|
||||
{:ok, Activity.t()} | {:error, any()}
|
||||
def follow(follower, followed, activity_id \\ nil, local \\ true) do
|
||||
|
|
|
@ -10,6 +10,8 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
@spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()}
|
||||
def emoji_react(actor, object, emoji) do
|
||||
with {:ok, data, meta} <- object_action(actor, object) do
|
||||
|
@ -83,6 +85,29 @@ def like(actor, object) do
|
|||
end
|
||||
end
|
||||
|
||||
def announce(actor, object, options \\ []) do
|
||||
public? = Keyword.get(options, :public, false)
|
||||
to = [actor.follower_address, object.data["actor"]]
|
||||
|
||||
to =
|
||||
if public? do
|
||||
[Pleroma.Constants.as_public() | to]
|
||||
else
|
||||
to
|
||||
end
|
||||
|
||||
{:ok,
|
||||
%{
|
||||
"id" => Utils.generate_activity_id(),
|
||||
"actor" => actor.ap_id,
|
||||
"object" => object.data["id"],
|
||||
"to" => to,
|
||||
"context" => object.data["context"],
|
||||
"type" => "Announce",
|
||||
"published" => Utils.make_date()
|
||||
}, []}
|
||||
end
|
||||
|
||||
@spec object_action(User.t(), Object.t()) :: {:ok, map(), keyword()}
|
||||
defp object_action(actor, object) do
|
||||
object_actor = User.get_cached_by_ap_id(object.data["actor"])
|
||||
|
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
|||
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
|
||||
|
@ -58,6 +59,16 @@ def validate(%{"type" => "EmojiReact"} = object, meta) do
|
|||
end
|
||||
end
|
||||
|
||||
def validate(%{"type" => "Announce"} = object, meta) do
|
||||
with {:ok, object} <-
|
||||
object
|
||||
|> AnnounceValidator.cast_and_validate()
|
||||
|> Ecto.Changeset.apply_action(:insert) do
|
||||
object = stringify_keys(object |> Map.from_struct())
|
||||
{:ok, object, meta}
|
||||
end
|
||||
end
|
||||
|
||||
def stringify_keys(%{__struct__: _} = object) do
|
||||
object
|
||||
|> Map.from_struct()
|
||||
|
@ -77,7 +88,7 @@ def fetch_actor(object) do
|
|||
|
||||
def fetch_actor_and_object(object) do
|
||||
fetch_actor(object)
|
||||
Object.normalize(object["object"])
|
||||
Object.normalize(object["object"], true)
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
|
||||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.Types
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
|
||||
import Ecto.Changeset
|
||||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
@primary_key false
|
||||
|
||||
embedded_schema do
|
||||
field(:id, Types.ObjectID, primary_key: true)
|
||||
field(:type, :string)
|
||||
field(:object, Types.ObjectID)
|
||||
field(:actor, Types.ObjectID)
|
||||
field(:context, :string, autogenerate: {Utils, :generate_context_id, []})
|
||||
field(:to, Types.Recipients, default: [])
|
||||
field(:cc, Types.Recipients, default: [])
|
||||
field(:published, Types.DateTime)
|
||||
end
|
||||
|
||||
def cast_and_validate(data) do
|
||||
data
|
||||
|> cast_data()
|
||||
|> validate_data()
|
||||
end
|
||||
|
||||
def cast_data(data) do
|
||||
%__MODULE__{}
|
||||
|> changeset(data)
|
||||
end
|
||||
|
||||
def changeset(struct, data) do
|
||||
struct
|
||||
|> cast(data, __schema__(:fields))
|
||||
|> fix_after_cast()
|
||||
end
|
||||
|
||||
def fix_after_cast(cng) do
|
||||
cng
|
||||
end
|
||||
|
||||
def validate_data(data_cng) do
|
||||
data_cng
|
||||
|> validate_inclusion(:type, ["Announce"])
|
||||
|> validate_required([:id, :type, :object, :actor, :to, :cc])
|
||||
|> validate_actor_presence()
|
||||
|> validate_object_presence()
|
||||
|> validate_existing_announce()
|
||||
|> validate_announcable()
|
||||
end
|
||||
|
||||
def validate_announcable(cng) do
|
||||
with actor when is_binary(actor) <- get_field(cng, :actor),
|
||||
object when is_binary(object) <- get_field(cng, :object),
|
||||
%User{} = actor <- User.get_cached_by_ap_id(actor),
|
||||
%Object{} = object <- Object.get_cached_by_ap_id(object),
|
||||
false <- Visibility.is_public?(object) do
|
||||
same_actor = object.data["actor"] == actor.ap_id
|
||||
is_public = Pleroma.Constants.as_public() in (get_field(cng, :to) ++ get_field(cng, :cc))
|
||||
|
||||
cond do
|
||||
same_actor && is_public ->
|
||||
cng
|
||||
|> add_error(:actor, "can not announce this object publicly")
|
||||
|
||||
!same_actor ->
|
||||
cng
|
||||
|> add_error(:actor, "can not announce this object")
|
||||
|
||||
true ->
|
||||
cng
|
||||
end
|
||||
else
|
||||
_ -> cng
|
||||
end
|
||||
end
|
||||
|
||||
def validate_existing_announce(cng) do
|
||||
actor = get_field(cng, :actor)
|
||||
object = get_field(cng, :object)
|
||||
|
||||
if actor && object && Utils.get_existing_announce(actor, %{data: %{"id" => object}}) do
|
||||
cng
|
||||
|> add_error(:actor, "already announced this object")
|
||||
|> add_error(:object, "already announced by this actor")
|
||||
else
|
||||
cng
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.Pipeline do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
@ -44,7 +45,7 @@ defp maybe_federate(%Object{}, _), do: {:ok, :not_federated}
|
|||
|
||||
defp maybe_federate(%Activity{} = activity, meta) do
|
||||
with {:ok, local} <- Keyword.fetch(meta, :local) do
|
||||
do_not_federate = meta[:do_not_federate]
|
||||
do_not_federate = meta[:do_not_federate] || !Config.get([:instance, :federating])
|
||||
|
||||
if !do_not_federate && local do
|
||||
Federator.publish(activity)
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.Relay do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.CommonAPI
|
||||
require Logger
|
||||
|
||||
@relay_nickname "relay"
|
||||
|
@ -48,11 +49,11 @@ def unfollow(target_instance) do
|
|||
end
|
||||
end
|
||||
|
||||
@spec publish(any()) :: {:ok, Activity.t(), Object.t()} | {:error, any()}
|
||||
@spec publish(any()) :: {:ok, Activity.t()} | {:error, any()}
|
||||
def publish(%Activity{data: %{"type" => "Create"}} = activity) do
|
||||
with %User{} = user <- get_actor(),
|
||||
%Object{} = object <- Object.normalize(activity) do
|
||||
ActivityPub.announce(user, object, nil, true, false)
|
||||
true <- Visibility.is_public?(activity) do
|
||||
CommonAPI.repeat(activity.id, user)
|
||||
else
|
||||
error -> format_error(error)
|
||||
end
|
||||
|
|
|
@ -27,6 +27,21 @@ def handle(%{data: %{"type" => "Like"}} = object, meta) do
|
|||
{:ok, object, meta}
|
||||
end
|
||||
|
||||
# Tasks this handles:
|
||||
# - Add announce to object
|
||||
# - Set up notification
|
||||
# - Stream out the announce
|
||||
def handle(%{data: %{"type" => "Announce"}} = object, meta) do
|
||||
announced_object = Object.get_by_ap_id(object.data["object"])
|
||||
|
||||
Utils.add_announce_to_object(object, announced_object)
|
||||
|
||||
Notification.create_notifications(object)
|
||||
ActivityPub.stream_out(object)
|
||||
|
||||
{:ok, object, meta}
|
||||
end
|
||||
|
||||
def handle(%{data: %{"type" => "Undo", "object" => undone_object}} = object, meta) do
|
||||
with undone_object <- Activity.get_by_ap_id(undone_object),
|
||||
:ok <- handle_undoing(undone_object) do
|
||||
|
|
|
@ -662,7 +662,8 @@ def handle_incoming(
|
|||
|> handle_incoming(options)
|
||||
end
|
||||
|
||||
def handle_incoming(%{"type" => type} = data, _options) when type in ["Like", "EmojiReact"] do
|
||||
def handle_incoming(%{"type" => type} = data, _options)
|
||||
when type in ["Like", "EmojiReact", "Announce"] do
|
||||
with :ok <- ObjectValidator.fetch_actor_and_object(data),
|
||||
{:ok, activity, _meta} <-
|
||||
Pipeline.common_pipeline(data, local: false) do
|
||||
|
@ -672,21 +673,6 @@ def handle_incoming(%{"type" => type} = data, _options) when type in ["Like", "E
|
|||
end
|
||||
end
|
||||
|
||||
def handle_incoming(
|
||||
%{"type" => "Announce", "object" => object_id, "actor" => _actor, "id" => id} = data,
|
||||
_options
|
||||
) do
|
||||
with actor <- Containment.get_actor(data),
|
||||
{:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
|
||||
{:ok, object} <- get_embedded_obj_helper(object_id, actor),
|
||||
public <- Visibility.is_public?(data),
|
||||
{:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do
|
||||
{:ok, activity}
|
||||
else
|
||||
_e -> :error
|
||||
end
|
||||
end
|
||||
|
||||
def handle_incoming(
|
||||
%{"type" => "Update", "object" => %{"type" => object_type} = object, "actor" => actor_id} =
|
||||
data,
|
||||
|
|
|
@ -127,18 +127,19 @@ def delete(activity_id, user) do
|
|||
end
|
||||
|
||||
def repeat(id, user, params \\ %{}) do
|
||||
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id) do
|
||||
object = Object.normalize(activity)
|
||||
announce_activity = Utils.get_existing_announce(user.ap_id, object)
|
||||
public = public_announce?(object, params)
|
||||
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
|
||||
object = %Object{} <- Object.normalize(activity, false),
|
||||
{_, nil} <- {:existing_announce, Utils.get_existing_announce(user.ap_id, object)},
|
||||
public = public_announce?(object, params),
|
||||
{:ok, announce, _} <- Builder.announce(user, object, public: public),
|
||||
{:ok, activity, _} <- Pipeline.common_pipeline(announce, local: true) do
|
||||
{:ok, activity}
|
||||
else
|
||||
{:existing_announce, %Activity{} = announce} ->
|
||||
{:ok, announce}
|
||||
|
||||
if announce_activity do
|
||||
{:ok, announce_activity, object}
|
||||
else
|
||||
ActivityPub.announce(user, object, nil, true, public)
|
||||
end
|
||||
else
|
||||
_ -> {:error, :not_found}
|
||||
_ ->
|
||||
{:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -210,7 +210,7 @@ def delete(%{assigns: %{user: user}} = conn, %{id: id}) do
|
|||
|
||||
@doc "POST /api/v1/statuses/:id/reblog"
|
||||
def reblog(%{assigns: %{user: user}, body_params: params} = conn, %{id: ap_id_or_id}) do
|
||||
with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user, params),
|
||||
with {:ok, announce} <- CommonAPI.repeat(ap_id_or_id, user, params),
|
||||
%Activity{} = announce <- Activity.normalize(announce.data) do
|
||||
try_render(conn, "show.json", %{activity: announce, for: user, as: :activity})
|
||||
end
|
||||
|
|
|
@ -1 +1,88 @@
|
|||
{"@context":["https://www.w3.org/ns/activitystreams","https://puckipedia.com/-/context"],"actor":{"endpoints":"https://puckipedia.com/#endpoints","followers":"https://puckipedia.com/followers","following":"https://puckipedia.com/following","icon":{"mediaType":"image/png","type":"Image","url":"https://puckipedia.com/images/avatar.png"},"id":"https://puckipedia.com/","inbox":"https://puckipedia.com/inbox","kroeg:blocks":{"id":"https://puckipedia.com/blocks"},"liked":"https://puckipedia.com/liked","manuallyApprovesFollowers":false,"name":"HACKER TEEN PUCKIPEDIA 👩💻","outbox":"https://puckipedia.com/outbox","preferredUsername":"puckipedia","publicKey":{"id":"https://puckipedia.com/#key","owner":"https://puckipedia.com/","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvN05xIcFE0Qgany7Rht4\n0ZI5wu++IT7K5iSqRimBYkpoeHbVcT9RFlW+aWH/QJJW/YgZ7+LMr8AMCrKrwSpS\nCndyrpx4O4lZ3FNRLu7tbklh01rGZfE6R1SFfYBpvMvImc9nYT6iezYDbv6NkHku\no3aVhjql216XlA0OhIrqQme9sAdrLbjbMrTUS8douCTkDOX+JFj1ghHCqdYEMZJI\nOY9kovtgnqyxFLm0RsPGsO1+g/OVojqG+VqHz6O2lceaTVQLlnZ4gOhLVG1tVsA2\nRfXQK+R/VgXncYE+BlQVd/tcdGAz7CDL7PP3rP65gmARnafhGR96cCOi/KzlAXSO\nMwIDAQAB\n-----END PUBLIC KEY-----","type":[]},"summary":"<p>federated hacker teen<br/>\n[<a href=\"https://pronoun.is/she\">she</a>/<a href=\"https://pronoun.is/they\">they</a>]</p>","type":"Person","updated":"2017-12-19T16:56:29.7576707+00:00"},"cc":"http://mastodon.example.org/users/admin","id":"https://puckipedia.com/cc56a9658e","object":{"as:sensitive":false,"attributedTo":{"endpoints":{"sharedInbox":"https://mastodon.social/inbox","type":[]},"followers":"http://mastodon.example.org/users/admin/followers","following":"http://mastodon.example.org/users/admin/following","icon":{"mediaType":"image/png","type":"Image","url":"https://files.mastodon.social/accounts/avatars/000/015/163/original/70ca6c52b01ca913.png"},"id":"http://mastodon.example.org/users/admin","inbox":"http://mastodon.example.org/users/admin/inbox","manuallyApprovesFollowers":{"@value":"False","type":"xsd:boolean"},"name":"","outbox":"http://mastodon.example.org/users/admin/outbox","preferredUsername":"revenant","publicKey":{"id":"http://mastodon.example.org/users/admin#main-key","owner":"http://mastodon.example.org/users/admin","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0gEN3wPW7gkE2gQqnmfB\n1ychjmFIf2LIwY0oCJLiGE/xpZrUKoq+eWH30AP7mATw4LD0gOYABL/ijqPUrPqR\nDXLL+0CqMP8HsZKvRlj9KArMK3YtNiSGGj2U7iReiRrD7nJzjJlsjjJXflLZhZ7/\nenSv1CcaeK8tB0PoAgShy/MyfhPF7WI5/Zm9DmmDQFvUEnDYKXAf/vG/IWw1EyMC\nkbaEYJeIowQU3GsbPxzRGI22bQtfotm431Ch2MbNo+kyzmYVFLAVoSGNMzvJwOPg\nTxLIIBeQXG7MinRyK887yPKhxhcALea4yCcALaa+3jPE7yqwIKYwTHtSlblsHDAo\nmQIDAQAB\n-----END PUBLIC KEY-----\n","type":[]},"summary":"<p>neatly partitioned meats and cheeses appeal to me on an aesthetic level | any pronouns | revenant1.net</p>","type":"Person","url":"https://mastodon.social/@revenant"},"cc":"http://mastodon.example.org/users/admin/followers","content":"<p>the name's jond (jeans bond)</p>","contentMap":{"en":"<p>the name's jond (jeans bond)</p>"},"conversation":"tag:mastodon.social,2018-09-25:objectId=55659382:objectType=Conversation","id":"http://mastodon.example.org/users/admin/statuses/100787282858396771","ostatus:atomUri":"http://mastodon.example.org/users/admin/statuses/100787282858396771","published":"2018-09-25T16:11:29Z","to":"https://www.w3.org/ns/activitystreams#Public","type":"Note","url":"https://mastodon.social/@revenant/100787282858396771"},"to":["https://www.w3.org/ns/activitystreams#Public","https://puckipedia.com/followers"],"type":"Announce"}
|
||||
{
|
||||
"@context" : [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://puckipedia.com/-/context"
|
||||
],
|
||||
"actor" : {
|
||||
"endpoints" : "https://puckipedia.com/#endpoints",
|
||||
"followers" : "https://puckipedia.com/followers",
|
||||
"following" : "https://puckipedia.com/following",
|
||||
"icon" : {
|
||||
"mediaType" : "image/png",
|
||||
"type" : "Image",
|
||||
"url" : "https://puckipedia.com/images/avatar.png"
|
||||
},
|
||||
"id" : "https://puckipedia.com/",
|
||||
"inbox" : "https://puckipedia.com/inbox",
|
||||
"kroeg:blocks" : {
|
||||
"id" : "https://puckipedia.com/blocks"
|
||||
},
|
||||
"liked" : "https://puckipedia.com/liked",
|
||||
"manuallyApprovesFollowers" : false,
|
||||
"name" : "HACKER TEEN PUCKIPEDIA ð©âð»",
|
||||
"outbox" : "https://puckipedia.com/outbox",
|
||||
"preferredUsername" : "puckipedia",
|
||||
"publicKey" : {
|
||||
"id" : "https://puckipedia.com/#key",
|
||||
"owner" : "https://puckipedia.com/",
|
||||
"publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvN05xIcFE0Qgany7Rht4\n0ZI5wu++IT7K5iSqRimBYkpoeHbVcT9RFlW+aWH/QJJW/YgZ7+LMr8AMCrKrwSpS\nCndyrpx4O4lZ3FNRLu7tbklh01rGZfE6R1SFfYBpvMvImc9nYT6iezYDbv6NkHku\no3aVhjql216XlA0OhIrqQme9sAdrLbjbMrTUS8douCTkDOX+JFj1ghHCqdYEMZJI\nOY9kovtgnqyxFLm0RsPGsO1+g/OVojqG+VqHz6O2lceaTVQLlnZ4gOhLVG1tVsA2\nRfXQK+R/VgXncYE+BlQVd/tcdGAz7CDL7PP3rP65gmARnafhGR96cCOi/KzlAXSO\nMwIDAQAB\n-----END PUBLIC KEY-----",
|
||||
"type" : []
|
||||
},
|
||||
"summary" : "<p>federated hacker teen<br/>\n[<a href=\"https://pronoun.is/she\">she</a>/<a href=\"https://pronoun.is/they\">they</a>]</p>",
|
||||
"type" : "Person",
|
||||
"updated" : "2017-12-19T16:56:29.7576707+00:00"
|
||||
},
|
||||
"cc" : "http://mastodon.example.org/users/admin",
|
||||
"id" : "https://puckipedia.com/cc56a9658e",
|
||||
"object" : {
|
||||
"as:sensitive" : false,
|
||||
"attributedTo" : {
|
||||
"endpoints" : {
|
||||
"sharedInbox" : "https://mastodon.social/inbox",
|
||||
"type" : []
|
||||
},
|
||||
"followers" : "http://mastodon.example.org/users/admin/followers",
|
||||
"following" : "http://mastodon.example.org/users/admin/following",
|
||||
"icon" : {
|
||||
"mediaType" : "image/png",
|
||||
"type" : "Image",
|
||||
"url" : "https://files.mastodon.social/accounts/avatars/000/015/163/original/70ca6c52b01ca913.png"
|
||||
},
|
||||
"id" : "http://mastodon.example.org/users/admin",
|
||||
"inbox" : "http://mastodon.example.org/users/admin/inbox",
|
||||
"manuallyApprovesFollowers" : {
|
||||
"@value" : "False",
|
||||
"type" : "xsd:boolean"
|
||||
},
|
||||
"name" : "",
|
||||
"outbox" : "http://mastodon.example.org/users/admin/outbox",
|
||||
"preferredUsername" : "revenant",
|
||||
"publicKey" : {
|
||||
"id" : "http://mastodon.example.org/users/admin#main-key",
|
||||
"owner" : "http://mastodon.example.org/users/admin",
|
||||
"publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0gEN3wPW7gkE2gQqnmfB\n1ychjmFIf2LIwY0oCJLiGE/xpZrUKoq+eWH30AP7mATw4LD0gOYABL/ijqPUrPqR\nDXLL+0CqMP8HsZKvRlj9KArMK3YtNiSGGj2U7iReiRrD7nJzjJlsjjJXflLZhZ7/\nenSv1CcaeK8tB0PoAgShy/MyfhPF7WI5/Zm9DmmDQFvUEnDYKXAf/vG/IWw1EyMC\nkbaEYJeIowQU3GsbPxzRGI22bQtfotm431Ch2MbNo+kyzmYVFLAVoSGNMzvJwOPg\nTxLIIBeQXG7MinRyK887yPKhxhcALea4yCcALaa+3jPE7yqwIKYwTHtSlblsHDAo\nmQIDAQAB\n-----END PUBLIC KEY-----\n",
|
||||
"type" : []
|
||||
},
|
||||
"summary" : "<p>neatly partitioned meats and cheeses appeal to me on an aesthetic level | any pronouns | revenant1.net</p>",
|
||||
"type" : "Person",
|
||||
"url" : "https://mastodon.social/@revenant"
|
||||
},
|
||||
"cc" : "http://mastodon.example.org/users/admin/followers",
|
||||
"content" : "<p>the name's jond (jeans bond)</p>",
|
||||
"contentMap" : {
|
||||
"en" : "<p>the name's jond (jeans bond)</p>"
|
||||
},
|
||||
"conversation" : "tag:mastodon.social,2018-09-25:objectId=55659382:objectType=Conversation",
|
||||
"id" : "http://mastodon.example.org/users/admin/statuses/100787282858396771",
|
||||
"ostatus:atomUri" : "http://mastodon.example.org/users/admin/statuses/100787282858396771",
|
||||
"published" : "2018-09-25T16:11:29Z",
|
||||
"to" : "https://www.w3.org/ns/activitystreams#Public",
|
||||
"type" : "Note",
|
||||
"url" : "https://mastodon.social/@revenant/100787282858396771"
|
||||
},
|
||||
"to" : [
|
||||
"https://www.w3.org/ns/activitystreams#Public",
|
||||
"https://puckipedia.com/followers"
|
||||
],
|
||||
"type" : "Announce"
|
||||
}
|
||||
|
|
46
test/fixtures/mastodon-note-object.json
vendored
46
test/fixtures/mastodon-note-object.json
vendored
|
@ -1,9 +1,45 @@
|
|||
{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","sensitive":"as:sensitive","movedTo":"as:movedTo","Hashtag":"as:Hashtag","ostatus":"http://ostatus.org#","atomUri":"ostatus:atomUri","inReplyToAtomUri":"ostatus:inReplyToAtomUri","conversation":"ostatus:conversation","toot":"http://joinmastodon.org/ns#","Emoji":"toot:Emoji"}],"id":"http://mastodon.example.org/users/admin/statuses/99541947525187367","type":"Note","summary":null,"content":"\u003cp\u003eyeah.\u003c/p\u003e","inReplyTo":null,"published":"2018-02-17T17:46:20Z","url":"http://mastodon.example.org/@admin/99541947525187367","attributedTo":"http://mastodon.example.org/users/admin","to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["http://mastodon.example.org/users/admin/followers"],"sensitive":false,"atomUri":"http://mastodon.example.org/users/admin/statuses/99541947525187367","inReplyToAtomUri":null,"conversation":"tag:mastodon.example.org,2018-02-17:objectId=59:objectType=Conversation","tag":[],
|
||||
{
|
||||
"@context" : [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"Emoji" : "toot:Emoji",
|
||||
"Hashtag" : "as:Hashtag",
|
||||
"atomUri" : "ostatus:atomUri",
|
||||
"conversation" : "ostatus:conversation",
|
||||
"inReplyToAtomUri" : "ostatus:inReplyToAtomUri",
|
||||
"manuallyApprovesFollowers" : "as:manuallyApprovesFollowers",
|
||||
"movedTo" : "as:movedTo",
|
||||
"ostatus" : "http://ostatus.org#",
|
||||
"sensitive" : "as:sensitive",
|
||||
"toot" : "http://joinmastodon.org/ns#"
|
||||
}
|
||||
],
|
||||
"atomUri" : "http://mastodon.example.org/users/admin/statuses/99541947525187367",
|
||||
"attachment" : [
|
||||
{
|
||||
"url": "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg",
|
||||
"type": "Document",
|
||||
"mediaType" : "image/jpeg",
|
||||
"name" : null,
|
||||
"mediaType": "image/jpeg"
|
||||
"type" : "Document",
|
||||
"url" : "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg"
|
||||
}
|
||||
],
|
||||
"attributedTo" : "http://mastodon.example.org/users/admin",
|
||||
"cc" : [
|
||||
"http://mastodon.example.org/users/admin/followers"
|
||||
],
|
||||
"content" : "<p>yeah.</p>",
|
||||
"conversation" : "tag:mastodon.example.org,2018-02-17:objectId=59:objectType=Conversation",
|
||||
"id" : "http://mastodon.example.org/users/admin/statuses/99541947525187367",
|
||||
"inReplyTo" : null,
|
||||
"inReplyToAtomUri" : null,
|
||||
"published" : "2018-02-17T17:46:20Z",
|
||||
"sensitive" : false,
|
||||
"summary" : null,
|
||||
"tag" : [],
|
||||
"to" : [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"type" : "Note",
|
||||
"url" : "http://mastodon.example.org/@admin/99541947525187367"
|
||||
}
|
||||
]}
|
||||
|
|
|
@ -648,7 +648,7 @@ test "it does not send notification to mentioned users in announces" do
|
|||
status: "hey @#{other_user.nickname}!"
|
||||
})
|
||||
|
||||
{:ok, activity_two, _} = CommonAPI.repeat(activity_one.id, third_user)
|
||||
{:ok, activity_two} = CommonAPI.repeat(activity_one.id, third_user)
|
||||
|
||||
{enabled_receivers, _disabled_receivers} =
|
||||
Notification.get_notified_from_activity(activity_two)
|
||||
|
@ -778,7 +778,7 @@ test "repeating an activity results in 1 notification, then 0 if the activity is
|
|||
|
||||
assert Enum.empty?(Notification.for_user(user))
|
||||
|
||||
{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
|
||||
{:ok, _} = CommonAPI.repeat(activity.id, other_user)
|
||||
|
||||
assert length(Notification.for_user(user)) == 1
|
||||
|
||||
|
@ -795,7 +795,7 @@ test "repeating an activity results in 1 notification, then 0 if the activity is
|
|||
|
||||
assert Enum.empty?(Notification.for_user(user))
|
||||
|
||||
{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
|
||||
{:ok, _} = CommonAPI.repeat(activity.id, other_user)
|
||||
|
||||
assert length(Notification.for_user(user)) == 1
|
||||
|
||||
|
|
|
@ -91,6 +91,7 @@ test "user is not created" do
|
|||
|
||||
describe "running rm" do
|
||||
test "user is deleted" do
|
||||
clear_config([:instance, :federating], true)
|
||||
user = insert(:user)
|
||||
|
||||
with_mock Pleroma.Web.Federator,
|
||||
|
@ -108,8 +109,10 @@ test "user is deleted" do
|
|||
|
||||
test "a remote user's create activity is deleted when the object has been pruned" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, post} = CommonAPI.post(user, %{status: "uguu"})
|
||||
|
||||
clear_config([:instance, :federating], true)
|
||||
|
||||
object = Object.normalize(post)
|
||||
Object.prune(object)
|
||||
|
||||
|
|
|
@ -992,7 +992,7 @@ test "works for announces" do
|
|||
user = insert(:user, local: true)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(actor, %{status: "hello"})
|
||||
{:ok, announce, _} = CommonAPI.repeat(activity.id, user)
|
||||
{:ok, announce} = CommonAPI.repeat(activity.id, user)
|
||||
|
||||
recipients = User.get_recipients_from_activity(announce)
|
||||
|
||||
|
@ -1147,7 +1147,7 @@ test "it deactivates a user, all follow relationships and all activities", %{use
|
|||
|
||||
{:ok, like} = CommonAPI.favorite(user, activity_two.id)
|
||||
{:ok, like_two} = CommonAPI.favorite(follower, activity.id)
|
||||
{:ok, repeat, _} = CommonAPI.repeat(activity_two.id, user)
|
||||
{:ok, repeat} = CommonAPI.repeat(activity_two.id, user)
|
||||
|
||||
{:ok, job} = User.delete(user)
|
||||
{:ok, _user} = ObanHelpers.perform(job)
|
||||
|
|
|
@ -537,7 +537,7 @@ test "doesn't return blocked activities" do
|
|||
assert Enum.member?(activities, activity_one)
|
||||
|
||||
{:ok, _user_relationship} = User.block(user, %{ap_id: activity_three.data["actor"]})
|
||||
{:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
|
||||
{:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
|
||||
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
|
||||
activity_three = Activity.get_by_id(activity_three.id)
|
||||
|
||||
|
@ -592,7 +592,7 @@ test "doesn't return announce activities concerning blocked users" do
|
|||
|
||||
{:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
|
||||
|
||||
{:ok, activity_three, _} = CommonAPI.repeat(activity_two.id, friend)
|
||||
{:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)
|
||||
|
||||
activities =
|
||||
ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
|
||||
|
@ -618,7 +618,7 @@ test "doesn't return activities from blocked domains" do
|
|||
|
||||
followed_user = insert(:user)
|
||||
ActivityPub.follow(user, followed_user)
|
||||
{:ok, repeat_activity, _} = CommonAPI.repeat(activity.id, followed_user)
|
||||
{:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user)
|
||||
|
||||
activities =
|
||||
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
|
||||
|
@ -651,7 +651,7 @@ test "does return activities from followed users on blocked domains" do
|
|||
another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"})
|
||||
bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
|
||||
bad_activity = insert(:note_activity, %{note: bad_note})
|
||||
{:ok, repeat_activity, _} = CommonAPI.repeat(bad_activity.id, domain_user)
|
||||
{:ok, repeat_activity} = CommonAPI.repeat(bad_activity.id, domain_user)
|
||||
|
||||
activities =
|
||||
ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true})
|
||||
|
@ -699,7 +699,7 @@ test "doesn't return muted activities" do
|
|||
|
||||
activity_three_actor = User.get_by_ap_id(activity_three.data["actor"])
|
||||
{:ok, _user_relationships} = User.mute(user, activity_three_actor)
|
||||
{:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
|
||||
{:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
|
||||
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
|
||||
activity_three = Activity.get_by_id(activity_three.id)
|
||||
|
||||
|
@ -749,7 +749,7 @@ test "does include announces on request" do
|
|||
|
||||
{:ok, user} = User.follow(user, booster)
|
||||
|
||||
{:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster)
|
||||
{:ok, announce} = CommonAPI.repeat(activity_three.id, booster)
|
||||
|
||||
[announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
|
||||
|
||||
|
@ -846,7 +846,7 @@ test "doesn't return reblogs for users for whom reblogs have been muted" do
|
|||
booster = insert(:user)
|
||||
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
|
||||
|
||||
{:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
|
||||
{:ok, activity} = CommonAPI.repeat(activity.id, booster)
|
||||
|
||||
activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
|
||||
|
||||
|
@ -860,7 +860,7 @@ test "returns reblogs for users for whom reblogs have not been muted" do
|
|||
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
|
||||
{:ok, _reblog_mute} = CommonAPI.show_reblogs(user, booster)
|
||||
|
||||
{:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
|
||||
{:ok, activity} = CommonAPI.repeat(activity.id, booster)
|
||||
|
||||
activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
|
||||
|
||||
|
@ -868,75 +868,6 @@ test "returns reblogs for users for whom reblogs have not been muted" do
|
|||
end
|
||||
end
|
||||
|
||||
describe "announcing an object" do
|
||||
test "adds an announce activity to the db" do
|
||||
note_activity = insert(:note_activity)
|
||||
object = Object.normalize(note_activity)
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, announce_activity, object} = ActivityPub.announce(user, object)
|
||||
assert object.data["announcement_count"] == 1
|
||||
assert object.data["announcements"] == [user.ap_id]
|
||||
|
||||
assert announce_activity.data["to"] == [
|
||||
User.ap_followers(user),
|
||||
note_activity.data["actor"]
|
||||
]
|
||||
|
||||
assert announce_activity.data["object"] == object.data["id"]
|
||||
assert announce_activity.data["actor"] == user.ap_id
|
||||
assert announce_activity.data["context"] == object.data["context"]
|
||||
end
|
||||
|
||||
test "reverts annouce from object on error" do
|
||||
note_activity = insert(:note_activity)
|
||||
object = Object.normalize(note_activity)
|
||||
user = insert(:user)
|
||||
|
||||
with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
|
||||
assert {:error, :reverted} = ActivityPub.announce(user, object)
|
||||
end
|
||||
|
||||
reloaded_object = Object.get_by_ap_id(object.data["id"])
|
||||
assert reloaded_object == object
|
||||
refute reloaded_object.data["announcement_count"]
|
||||
refute reloaded_object.data["announcements"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "announcing a private object" do
|
||||
test "adds an announce activity to the db if the audience is not widened" do
|
||||
user = insert(:user)
|
||||
{:ok, note_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
|
||||
object = Object.normalize(note_activity)
|
||||
|
||||
{:ok, announce_activity, object} = ActivityPub.announce(user, object, nil, true, false)
|
||||
|
||||
assert announce_activity.data["to"] == [User.ap_followers(user)]
|
||||
|
||||
assert announce_activity.data["object"] == object.data["id"]
|
||||
assert announce_activity.data["actor"] == user.ap_id
|
||||
assert announce_activity.data["context"] == object.data["context"]
|
||||
end
|
||||
|
||||
test "does not add an announce activity to the db if the audience is widened" do
|
||||
user = insert(:user)
|
||||
{:ok, note_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
|
||||
object = Object.normalize(note_activity)
|
||||
|
||||
assert {:error, _} = ActivityPub.announce(user, object, nil, true, true)
|
||||
end
|
||||
|
||||
test "does not add an announce activity to the db if the announcer is not the author" do
|
||||
user = insert(:user)
|
||||
announcer = insert(:user)
|
||||
{:ok, note_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
|
||||
object = Object.normalize(note_activity)
|
||||
|
||||
assert {:error, _} = ActivityPub.announce(announcer, object, nil, true, false)
|
||||
end
|
||||
end
|
||||
|
||||
describe "uploading files" do
|
||||
test "copies the file to the configured folder" do
|
||||
file = %Plug.Upload{
|
||||
|
|
|
@ -280,4 +280,96 @@ test "it works when actor or object are wrapped in maps", %{valid_like: valid_li
|
|||
assert {:object, valid_like["object"]} in validated.changes
|
||||
end
|
||||
end
|
||||
|
||||
describe "announces" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
announcer = insert(:user)
|
||||
{:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"})
|
||||
|
||||
object = Object.normalize(post_activity, false)
|
||||
{:ok, valid_announce, []} = Builder.announce(announcer, object)
|
||||
|
||||
%{
|
||||
valid_announce: valid_announce,
|
||||
user: user,
|
||||
post_activity: post_activity,
|
||||
announcer: announcer
|
||||
}
|
||||
end
|
||||
|
||||
test "returns ok for a valid announce", %{valid_announce: valid_announce} do
|
||||
assert {:ok, _object, _meta} = ObjectValidator.validate(valid_announce, [])
|
||||
end
|
||||
|
||||
test "returns an error if the object can't be found", %{valid_announce: valid_announce} do
|
||||
without_object =
|
||||
valid_announce
|
||||
|> Map.delete("object")
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(without_object, [])
|
||||
|
||||
assert {:object, {"can't be blank", [validation: :required]}} in cng.errors
|
||||
|
||||
nonexisting_object =
|
||||
valid_announce
|
||||
|> Map.put("object", "https://gensokyo.2hu/objects/99999999")
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(nonexisting_object, [])
|
||||
|
||||
assert {:object, {"can't find object", []}} in cng.errors
|
||||
end
|
||||
|
||||
test "returns an error if we don't have the actor", %{valid_announce: valid_announce} do
|
||||
nonexisting_actor =
|
||||
valid_announce
|
||||
|> Map.put("actor", "https://gensokyo.2hu/users/raymoo")
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(nonexisting_actor, [])
|
||||
|
||||
assert {:actor, {"can't find user", []}} in cng.errors
|
||||
end
|
||||
|
||||
test "returns an error if the actor already announced the object", %{
|
||||
valid_announce: valid_announce,
|
||||
announcer: announcer,
|
||||
post_activity: post_activity
|
||||
} do
|
||||
_announce = CommonAPI.repeat(post_activity.id, announcer)
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(valid_announce, [])
|
||||
|
||||
assert {:actor, {"already announced this object", []}} in cng.errors
|
||||
assert {:object, {"already announced by this actor", []}} in cng.errors
|
||||
end
|
||||
|
||||
test "returns an error if the actor can't announce the object", %{
|
||||
announcer: announcer,
|
||||
user: user
|
||||
} do
|
||||
{:ok, post_activity} =
|
||||
CommonAPI.post(user, %{status: "a secret post", visibility: "private"})
|
||||
|
||||
object = Object.normalize(post_activity, false)
|
||||
|
||||
# Another user can't announce it
|
||||
{:ok, announce, []} = Builder.announce(announcer, object, public: false)
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(announce, [])
|
||||
|
||||
assert {:actor, {"can not announce this object", []}} in cng.errors
|
||||
|
||||
# The actor of the object can announce it
|
||||
{:ok, announce, []} = Builder.announce(user, object, public: false)
|
||||
|
||||
assert {:ok, _, _} = ObjectValidator.validate(announce, [])
|
||||
|
||||
# The actor of the object can not announce it publicly
|
||||
{:ok, announce, []} = Builder.announce(user, object, public: true)
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(announce, [])
|
||||
|
||||
assert {:actor, {"can not announce this object publicly", []}} in cng.errors
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,6 +9,11 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do
|
|||
import Pleroma.Factory
|
||||
|
||||
describe "common_pipeline/2" do
|
||||
setup do
|
||||
clear_config([:instance, :federating], true)
|
||||
:ok
|
||||
end
|
||||
|
||||
test "it goes through validation, filtering, persisting, side effects and federation for local activities" do
|
||||
activity = insert(:note_activity)
|
||||
meta = [local: true]
|
||||
|
@ -83,5 +88,44 @@ test "it goes through validation, filtering, persisting, side effects without fe
|
|||
assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
|
||||
end
|
||||
end
|
||||
|
||||
test "it goes through validation, filtering, persisting, side effects without federation for local activities if federation is deactivated" do
|
||||
clear_config([:instance, :federating], false)
|
||||
|
||||
activity = insert(:note_activity)
|
||||
meta = [local: true]
|
||||
|
||||
with_mocks([
|
||||
{Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]},
|
||||
{
|
||||
Pleroma.Web.ActivityPub.MRF,
|
||||
[],
|
||||
[filter: fn o -> {:ok, o} end]
|
||||
},
|
||||
{
|
||||
Pleroma.Web.ActivityPub.ActivityPub,
|
||||
[],
|
||||
[persist: fn o, m -> {:ok, o, m} end]
|
||||
},
|
||||
{
|
||||
Pleroma.Web.ActivityPub.SideEffects,
|
||||
[],
|
||||
[handle: fn o, m -> {:ok, o, m} end]
|
||||
},
|
||||
{
|
||||
Pleroma.Web.Federator,
|
||||
[],
|
||||
[]
|
||||
}
|
||||
]) do
|
||||
assert {:ok, ^activity, ^meta} =
|
||||
Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta)
|
||||
|
||||
assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta))
|
||||
assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity))
|
||||
assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta))
|
||||
assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,7 +6,6 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
|
|||
use Pleroma.DataCase
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Relay
|
||||
|
@ -95,21 +94,20 @@ test "returns error when object is unknown" do
|
|||
end)
|
||||
|
||||
assert capture_log(fn ->
|
||||
assert Relay.publish(activity) == {:error, nil}
|
||||
end) =~ "[error] error: nil"
|
||||
assert Relay.publish(activity) == {:error, false}
|
||||
end) =~ "[error] error: false"
|
||||
end
|
||||
|
||||
test_with_mock "returns announce activity and publish to federate",
|
||||
Pleroma.Web.Federator,
|
||||
[:passthrough],
|
||||
[] do
|
||||
Pleroma.Config.put([:instance, :federating], true)
|
||||
clear_config([:instance, :federating], true)
|
||||
service_actor = Relay.get_actor()
|
||||
note = insert(:note_activity)
|
||||
assert {:ok, %Activity{} = activity, %Object{} = obj} = Relay.publish(note)
|
||||
assert {:ok, %Activity{} = activity} = Relay.publish(note)
|
||||
assert activity.data["type"] == "Announce"
|
||||
assert activity.data["actor"] == service_actor.ap_id
|
||||
assert activity.data["object"] == obj.data["id"]
|
||||
assert called(Pleroma.Web.Federator.publish(activity))
|
||||
end
|
||||
|
||||
|
@ -117,13 +115,12 @@ test "returns error when object is unknown" do
|
|||
Pleroma.Web.Federator,
|
||||
[:passthrough],
|
||||
[] do
|
||||
Pleroma.Config.put([:instance, :federating], false)
|
||||
clear_config([:instance, :federating], false)
|
||||
service_actor = Relay.get_actor()
|
||||
note = insert(:note_activity)
|
||||
assert {:ok, %Activity{} = activity, %Object{} = obj} = Relay.publish(note)
|
||||
assert {:ok, %Activity{} = activity} = Relay.publish(note)
|
||||
assert activity.data["type"] == "Announce"
|
||||
assert activity.data["actor"] == service_actor.ap_id
|
||||
assert activity.data["object"] == obj.data["id"]
|
||||
refute called(Pleroma.Web.Federator.publish(activity))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -172,7 +172,7 @@ test "when activation is required", %{delete: delete, user: user} do
|
|||
{:ok, post} = CommonAPI.post(poster, %{status: "hey"})
|
||||
{:ok, like} = CommonAPI.favorite(user, post.id)
|
||||
{:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍")
|
||||
{:ok, announce, _} = CommonAPI.repeat(post.id, user)
|
||||
{:ok, announce} = CommonAPI.repeat(post.id, user)
|
||||
{:ok, block} = ActivityPub.block(user, poster)
|
||||
User.block(user, poster)
|
||||
|
||||
|
@ -289,4 +289,61 @@ test "creates a notification", %{like: like, poster: poster} do
|
|||
assert Repo.get_by(Notification, user_id: poster.id, activity_id: like.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "announce objects" do
|
||||
setup do
|
||||
poster = insert(:user)
|
||||
user = insert(:user)
|
||||
{:ok, post} = CommonAPI.post(poster, %{status: "hey"})
|
||||
{:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
|
||||
|
||||
{:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true)
|
||||
|
||||
{:ok, private_announce_data, _meta} =
|
||||
Builder.announce(user, private_post.object, public: false)
|
||||
|
||||
{:ok, relay_announce_data, _meta} =
|
||||
Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true)
|
||||
|
||||
{:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true)
|
||||
{:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true)
|
||||
{:ok, relay_announce, _meta} = ActivityPub.persist(relay_announce_data, local: true)
|
||||
|
||||
%{
|
||||
announce: announce,
|
||||
user: user,
|
||||
poster: poster,
|
||||
private_announce: private_announce,
|
||||
relay_announce: relay_announce
|
||||
}
|
||||
end
|
||||
|
||||
test "adds the announce to the original object", %{announce: announce, user: user} do
|
||||
{:ok, announce, _} = SideEffects.handle(announce)
|
||||
object = Object.get_by_ap_id(announce.data["object"])
|
||||
assert object.data["announcement_count"] == 1
|
||||
assert user.ap_id in object.data["announcements"]
|
||||
end
|
||||
|
||||
test "does not add the announce to the original object if the actor is a service actor", %{
|
||||
relay_announce: announce
|
||||
} do
|
||||
{:ok, announce, _} = SideEffects.handle(announce)
|
||||
object = Object.get_by_ap_id(announce.data["object"])
|
||||
assert object.data["announcement_count"] == nil
|
||||
end
|
||||
|
||||
test "creates a notification", %{announce: announce, poster: poster} do
|
||||
{:ok, announce, _} = SideEffects.handle(announce)
|
||||
assert Repo.get_by(Notification, user_id: poster.id, activity_id: announce.id)
|
||||
end
|
||||
|
||||
test "it streams out the announce", %{announce: announce} do
|
||||
with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], stream_out: fn _ -> nil end do
|
||||
{:ok, announce, _} = SideEffects.handle(announce)
|
||||
|
||||
assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(announce))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
172
test/web/activity_pub/transmogrifier/announce_handling_test.exs
Normal file
172
test/web/activity_pub/transmogrifier/announce_handling_test.exs
Normal file
|
@ -0,0 +1,172 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnnounceHandlingTest do
|
||||
use Pleroma.DataCase
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
test "it works for incoming honk announces" do
|
||||
user = insert(:user, ap_id: "https://honktest/u/test", local: false)
|
||||
other_user = insert(:user)
|
||||
{:ok, post} = CommonAPI.post(other_user, %{status: "bonkeronk"})
|
||||
|
||||
announce = %{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"actor" => "https://honktest/u/test",
|
||||
"id" => "https://honktest/u/test/bonk/1793M7B9MQ48847vdx",
|
||||
"object" => post.data["object"],
|
||||
"published" => "2019-06-25T19:33:58Z",
|
||||
"to" => "https://www.w3.org/ns/activitystreams#Public",
|
||||
"type" => "Announce"
|
||||
}
|
||||
|
||||
{:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(announce)
|
||||
|
||||
object = Object.get_by_ap_id(post.data["object"])
|
||||
|
||||
assert length(object.data["announcements"]) == 1
|
||||
assert user.ap_id in object.data["announcements"]
|
||||
end
|
||||
|
||||
test "it works for incoming announces with actor being inlined (kroeg)" do
|
||||
data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!()
|
||||
|
||||
_user = insert(:user, local: false, ap_id: data["actor"]["id"])
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, post} = CommonAPI.post(other_user, %{status: "kroegeroeg"})
|
||||
|
||||
data =
|
||||
data
|
||||
|> put_in(["object", "id"], post.data["object"])
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == "https://puckipedia.com/"
|
||||
end
|
||||
|
||||
test "it works for incoming announces, fetching the announced object" do
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-announce.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", "http://mastodon.example.org/users/admin/statuses/99541947525187367")
|
||||
|
||||
Tesla.Mock.mock(fn
|
||||
%{method: :get} ->
|
||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/mastodon-note-object.json")}
|
||||
end)
|
||||
|
||||
_user = insert(:user, local: false, ap_id: data["actor"])
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||
assert data["type"] == "Announce"
|
||||
|
||||
assert data["id"] ==
|
||||
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
|
||||
|
||||
assert data["object"] ==
|
||||
"http://mastodon.example.org/users/admin/statuses/99541947525187367"
|
||||
|
||||
assert(Activity.get_create_by_object_ap_id(data["object"]))
|
||||
end
|
||||
|
||||
@tag capture_log: true
|
||||
test "it works for incoming announces with an existing activity" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-announce.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", activity.data["object"])
|
||||
|
||||
_user = insert(:user, local: false, ap_id: data["actor"])
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||
assert data["type"] == "Announce"
|
||||
|
||||
assert data["id"] ==
|
||||
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
|
||||
|
||||
assert data["object"] == activity.data["object"]
|
||||
|
||||
assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id
|
||||
end
|
||||
|
||||
# Ignore inlined activities for now
|
||||
@tag skip: true
|
||||
test "it works for incoming announces with an inlined activity" do
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-announce-private.json")
|
||||
|> Poison.decode!()
|
||||
|
||||
_user =
|
||||
insert(:user,
|
||||
local: false,
|
||||
ap_id: data["actor"],
|
||||
follower_address: data["actor"] <> "/followers"
|
||||
)
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||
assert data["type"] == "Announce"
|
||||
|
||||
assert data["id"] ==
|
||||
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
|
||||
|
||||
object = Object.normalize(data["object"])
|
||||
|
||||
assert object.data["id"] == "http://mastodon.example.org/@admin/99541947525187368"
|
||||
assert object.data["content"] == "this is a private toot"
|
||||
end
|
||||
|
||||
@tag capture_log: true
|
||||
test "it rejects incoming announces with an inlined activity from another origin" do
|
||||
Tesla.Mock.mock(fn
|
||||
%{method: :get} -> %Tesla.Env{status: 404, body: ""}
|
||||
end)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/bogus-mastodon-announce.json")
|
||||
|> Poison.decode!()
|
||||
|
||||
_user = insert(:user, local: false, ap_id: data["actor"])
|
||||
|
||||
assert {:error, e} = Transmogrifier.handle_incoming(data)
|
||||
end
|
||||
|
||||
test "it does not clobber the addressing on announce activities" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-announce.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", Object.normalize(activity).data["id"])
|
||||
|> Map.put("to", ["http://mastodon.example.org/users/admin/followers"])
|
||||
|> Map.put("cc", [])
|
||||
|
||||
_user =
|
||||
insert(:user,
|
||||
local: false,
|
||||
ap_id: data["actor"],
|
||||
follower_address: "http://mastodon.example.org/users/admin/followers"
|
||||
)
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["to"] == ["http://mastodon.example.org/users/admin/followers"]
|
||||
end
|
||||
end
|
|
@ -28,6 +28,63 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
setup do: clear_config([:instance, :max_remote_account_fields])
|
||||
|
||||
describe "handle_incoming" do
|
||||
test "it works for incoming notices with tag not being an array (kroeg)" do
|
||||
data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!()
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
object = Object.normalize(data["object"])
|
||||
|
||||
assert object.data["emoji"] == %{
|
||||
"icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
|
||||
}
|
||||
|
||||
data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!()
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
object = Object.normalize(data["object"])
|
||||
|
||||
assert "test" in object.data["tag"]
|
||||
end
|
||||
|
||||
test "it works for incoming notices with url not being a string (prismo)" do
|
||||
data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!()
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
object = Object.normalize(data["object"])
|
||||
|
||||
assert object.data["url"] == "https://prismo.news/posts/83"
|
||||
end
|
||||
|
||||
test "it cleans up incoming notices which are not really DMs" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
to = [user.ap_id, other_user.ap_id]
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("to", to)
|
||||
|> Map.put("cc", [])
|
||||
|
||||
object =
|
||||
data["object"]
|
||||
|> Map.put("to", to)
|
||||
|> Map.put("cc", [])
|
||||
|
||||
data = Map.put(data, "object", object)
|
||||
|
||||
{:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["to"] == []
|
||||
assert data["cc"] == to
|
||||
|
||||
object_data = Object.normalize(activity).data
|
||||
|
||||
assert object_data["to"] == []
|
||||
assert object_data["cc"] == to
|
||||
end
|
||||
|
||||
test "it ignores an incoming notice if we already have it" do
|
||||
activity = insert(:note_activity)
|
||||
|
||||
|
@ -260,172 +317,6 @@ test "it works for incoming notices with to/cc not being an array (kroeg)" do
|
|||
"<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>"
|
||||
end
|
||||
|
||||
test "it works for incoming honk announces" do
|
||||
_user = insert(:user, ap_id: "https://honktest/u/test", local: false)
|
||||
other_user = insert(:user)
|
||||
{:ok, post} = CommonAPI.post(other_user, %{status: "bonkeronk"})
|
||||
|
||||
announce = %{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"actor" => "https://honktest/u/test",
|
||||
"id" => "https://honktest/u/test/bonk/1793M7B9MQ48847vdx",
|
||||
"object" => post.data["object"],
|
||||
"published" => "2019-06-25T19:33:58Z",
|
||||
"to" => "https://www.w3.org/ns/activitystreams#Public",
|
||||
"type" => "Announce"
|
||||
}
|
||||
|
||||
{:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(announce)
|
||||
end
|
||||
|
||||
test "it works for incoming announces with actor being inlined (kroeg)" do
|
||||
data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!()
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == "https://puckipedia.com/"
|
||||
end
|
||||
|
||||
test "it works for incoming notices with tag not being an array (kroeg)" do
|
||||
data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!()
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
object = Object.normalize(data["object"])
|
||||
|
||||
assert object.data["emoji"] == %{
|
||||
"icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
|
||||
}
|
||||
|
||||
data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!()
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
object = Object.normalize(data["object"])
|
||||
|
||||
assert "test" in object.data["tag"]
|
||||
end
|
||||
|
||||
test "it works for incoming notices with url not being a string (prismo)" do
|
||||
data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!()
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
object = Object.normalize(data["object"])
|
||||
|
||||
assert object.data["url"] == "https://prismo.news/posts/83"
|
||||
end
|
||||
|
||||
test "it cleans up incoming notices which are not really DMs" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
to = [user.ap_id, other_user.ap_id]
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("to", to)
|
||||
|> Map.put("cc", [])
|
||||
|
||||
object =
|
||||
data["object"]
|
||||
|> Map.put("to", to)
|
||||
|> Map.put("cc", [])
|
||||
|
||||
data = Map.put(data, "object", object)
|
||||
|
||||
{:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["to"] == []
|
||||
assert data["cc"] == to
|
||||
|
||||
object_data = Object.normalize(activity).data
|
||||
|
||||
assert object_data["to"] == []
|
||||
assert object_data["cc"] == to
|
||||
end
|
||||
|
||||
test "it works for incoming announces" do
|
||||
data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||
assert data["type"] == "Announce"
|
||||
|
||||
assert data["id"] ==
|
||||
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
|
||||
|
||||
assert data["object"] ==
|
||||
"http://mastodon.example.org/users/admin/statuses/99541947525187367"
|
||||
|
||||
assert Activity.get_create_by_object_ap_id(data["object"])
|
||||
end
|
||||
|
||||
test "it works for incoming announces with an existing activity" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-announce.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", activity.data["object"])
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||
assert data["type"] == "Announce"
|
||||
|
||||
assert data["id"] ==
|
||||
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
|
||||
|
||||
assert data["object"] == activity.data["object"]
|
||||
|
||||
assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id
|
||||
end
|
||||
|
||||
test "it works for incoming announces with an inlined activity" do
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-announce-private.json")
|
||||
|> Poison.decode!()
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||
assert data["type"] == "Announce"
|
||||
|
||||
assert data["id"] ==
|
||||
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
|
||||
|
||||
object = Object.normalize(data["object"])
|
||||
|
||||
assert object.data["id"] == "http://mastodon.example.org/@admin/99541947525187368"
|
||||
assert object.data["content"] == "this is a private toot"
|
||||
end
|
||||
|
||||
@tag capture_log: true
|
||||
test "it rejects incoming announces with an inlined activity from another origin" do
|
||||
data =
|
||||
File.read!("test/fixtures/bogus-mastodon-announce.json")
|
||||
|> Poison.decode!()
|
||||
|
||||
assert :error = Transmogrifier.handle_incoming(data)
|
||||
end
|
||||
|
||||
test "it does not clobber the addressing on announce activities" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-announce.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", Object.normalize(activity).data["id"])
|
||||
|> Map.put("to", ["http://mastodon.example.org/users/admin/followers"])
|
||||
|> Map.put("cc", [])
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["to"] == ["http://mastodon.example.org/users/admin/followers"]
|
||||
end
|
||||
|
||||
test "it ensures that as:Public activities make it to their followers collection" do
|
||||
user = insert(:user)
|
||||
|
||||
|
@ -1188,7 +1079,7 @@ test "it inlines private announced objects" do
|
|||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hey", visibility: "private"})
|
||||
|
||||
{:ok, announce_activity, _} = CommonAPI.repeat(activity.id, user)
|
||||
{:ok, announce_activity} = CommonAPI.repeat(activity.id, user)
|
||||
|
||||
{:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data)
|
||||
|
||||
|
@ -1438,7 +1329,7 @@ test "it rejects activities which reference objects with bogus origins" do
|
|||
}
|
||||
|
||||
assert capture_log(fn ->
|
||||
:error = Transmogrifier.handle_incoming(data)
|
||||
{:error, _} = Transmogrifier.handle_incoming(data)
|
||||
end) =~ "Object containment failed"
|
||||
end
|
||||
|
||||
|
@ -1453,7 +1344,7 @@ test "it rejects activities which reference objects that have an incorrect attri
|
|||
}
|
||||
|
||||
assert capture_log(fn ->
|
||||
:error = Transmogrifier.handle_incoming(data)
|
||||
{:error, _} = Transmogrifier.handle_incoming(data)
|
||||
end) =~ "Object containment failed"
|
||||
end
|
||||
|
||||
|
@ -1468,7 +1359,7 @@ test "it rejects activities which reference objects that have an incorrect attri
|
|||
}
|
||||
|
||||
assert capture_log(fn ->
|
||||
:error = Transmogrifier.handle_incoming(data)
|
||||
{:error, _} = Transmogrifier.handle_incoming(data)
|
||||
end) =~ "Object containment failed"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -334,7 +334,7 @@ test "fetches existing announce" do
|
|||
assert object = Object.normalize(note_activity)
|
||||
actor = insert(:user)
|
||||
|
||||
{:ok, announce, _object} = ActivityPub.announce(actor, object)
|
||||
{:ok, announce} = CommonAPI.repeat(note_activity.id, actor)
|
||||
assert Utils.get_existing_announce(actor.ap_id, object) == announce
|
||||
end
|
||||
end
|
||||
|
|
|
@ -73,7 +73,7 @@ test "renders an announce activity" do
|
|||
object = Object.normalize(note)
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, announce_activity, _} = CommonAPI.repeat(note.id, user)
|
||||
{:ok, announce_activity} = CommonAPI.repeat(note.id, user)
|
||||
|
||||
result = ObjectView.render("object.json", %{object: announce_activity})
|
||||
|
||||
|
|
|
@ -148,6 +148,7 @@ test "GET /api/pleroma/admin/users/:nickname requires " <>
|
|||
describe "DELETE /api/pleroma/admin/users" do
|
||||
test "single user", %{admin: admin, conn: conn} do
|
||||
user = insert(:user)
|
||||
clear_config([:instance, :federating], true)
|
||||
|
||||
with_mock Pleroma.Web.Federator,
|
||||
publish: fn _ -> nil end do
|
||||
|
@ -2944,6 +2945,7 @@ test "proxy tuple ip", %{conn: conn} do
|
|||
assert ":proxy_url" in db
|
||||
end
|
||||
|
||||
@tag capture_log: true
|
||||
test "doesn't set keys not in the whitelist", %{conn: conn} do
|
||||
clear_config(:database_config_whitelist, [
|
||||
{:pleroma, :key1},
|
||||
|
@ -3096,7 +3098,7 @@ test "returns private statuses with godmode on", %{conn: conn, user: user} do
|
|||
test "excludes reblogs by default", %{conn: conn, user: user} do
|
||||
other_user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "."})
|
||||
{:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, other_user)
|
||||
{:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user)
|
||||
|
||||
conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
|
||||
assert json_response(conn_res, 200) |> length() == 0
|
||||
|
|
|
@ -41,6 +41,8 @@ test "it works with pruned objects" do
|
|||
|
||||
{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
|
||||
|
||||
clear_config([:instance, :federating], true)
|
||||
|
||||
Object.normalize(post, false)
|
||||
|> Object.prune()
|
||||
|
||||
|
@ -59,6 +61,8 @@ test "it allows users to delete their posts" do
|
|||
|
||||
{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
|
||||
|
||||
clear_config([:instance, :federating], true)
|
||||
|
||||
with_mock Pleroma.Web.Federator,
|
||||
publish: fn _ -> nil end do
|
||||
assert {:ok, delete} = CommonAPI.delete(post.id, user)
|
||||
|
@ -442,7 +446,8 @@ test "repeating a status" do
|
|||
|
||||
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
|
||||
|
||||
{:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, user)
|
||||
{:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, user)
|
||||
assert Visibility.is_public?(announce_activity)
|
||||
end
|
||||
|
||||
test "can't repeat a repeat" do
|
||||
|
@ -450,9 +455,9 @@ test "can't repeat a repeat" do
|
|||
other_user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
|
||||
|
||||
{:ok, %Activity{} = announce, _} = CommonAPI.repeat(activity.id, other_user)
|
||||
{:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, other_user)
|
||||
|
||||
refute match?({:ok, %Activity{}, _}, CommonAPI.repeat(announce.id, user))
|
||||
refute match?({:ok, %Activity{}}, CommonAPI.repeat(announce.id, user))
|
||||
end
|
||||
|
||||
test "repeating a status privately" do
|
||||
|
@ -461,10 +466,11 @@ test "repeating a status privately" do
|
|||
|
||||
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
|
||||
|
||||
{:ok, %Activity{} = announce_activity, _} =
|
||||
{:ok, %Activity{} = announce_activity} =
|
||||
CommonAPI.repeat(activity.id, user, %{visibility: "private"})
|
||||
|
||||
assert Visibility.is_private?(announce_activity)
|
||||
refute Visibility.visible_for_user?(announce_activity, nil)
|
||||
end
|
||||
|
||||
test "favoriting a status" do
|
||||
|
@ -484,8 +490,8 @@ test "retweeting a status twice returns the status" do
|
|||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
|
||||
{:ok, %Activity{} = announce, object} = CommonAPI.repeat(activity.id, user)
|
||||
{:ok, ^announce, ^object} = CommonAPI.repeat(activity.id, user)
|
||||
{:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, user)
|
||||
{:ok, ^announce} = CommonAPI.repeat(activity.id, user)
|
||||
end
|
||||
|
||||
test "favoriting a status twice returns ok, but without the like activity" do
|
||||
|
|
|
@ -256,7 +256,7 @@ test "respects blocks", %{user: user_one, conn: conn} do
|
|||
User.block(user_one, user_two)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user_two, %{status: "User one sux0rz"})
|
||||
{:ok, repeat, _} = CommonAPI.repeat(activity.id, user_three)
|
||||
{:ok, repeat} = CommonAPI.repeat(activity.id, user_three)
|
||||
|
||||
assert resp =
|
||||
conn
|
||||
|
@ -375,7 +375,7 @@ test "gets an users media", %{conn: conn} do
|
|||
|
||||
test "gets a user's statuses without reblogs", %{user: user, conn: conn} do
|
||||
{:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "HI!!!"})
|
||||
{:ok, _, _} = CommonAPI.repeat(post_id, user)
|
||||
{:ok, _} = CommonAPI.repeat(post_id, user)
|
||||
|
||||
conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=true")
|
||||
assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
|
||||
|
@ -678,7 +678,7 @@ test "following without reblogs" do
|
|||
assert %{"showing_reblogs" => false} = json_response_and_validate_schema(ret_conn, 200)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
|
||||
{:ok, %{id: reblog_id}, _} = CommonAPI.repeat(activity.id, followed)
|
||||
{:ok, %{id: reblog_id}} = CommonAPI.repeat(activity.id, followed)
|
||||
|
||||
assert [] ==
|
||||
conn
|
||||
|
|
|
@ -280,8 +280,8 @@ test "filters notifications for Announce activities" do
|
|||
{:ok, unlisted_activity} =
|
||||
CommonAPI.post(other_user, %{status: ".", visibility: "unlisted"})
|
||||
|
||||
{:ok, _, _} = CommonAPI.repeat(public_activity.id, user)
|
||||
{:ok, _, _} = CommonAPI.repeat(unlisted_activity.id, user)
|
||||
{:ok, _} = CommonAPI.repeat(public_activity.id, user)
|
||||
{:ok, _} = CommonAPI.repeat(unlisted_activity.id, user)
|
||||
|
||||
activity_ids =
|
||||
conn
|
||||
|
@ -301,7 +301,7 @@ test "filters notifications using exclude_types" do
|
|||
{:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"})
|
||||
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
|
||||
{:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
|
||||
{:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
|
||||
{:ok, reblog_activity} = CommonAPI.repeat(create_activity.id, other_user)
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
|
||||
|
||||
mention_notification_id = get_notification_id_by_activity(mention_activity)
|
||||
|
@ -339,7 +339,7 @@ test "filters notifications using include_types" do
|
|||
{:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"})
|
||||
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
|
||||
{:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
|
||||
{:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
|
||||
{:ok, reblog_activity} = CommonAPI.repeat(create_activity.id, other_user)
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
|
||||
|
||||
mention_notification_id = get_notification_id_by_activity(mention_activity)
|
||||
|
|
|
@ -878,8 +878,8 @@ test "reblogged status for another user" do
|
|||
user3 = insert(:user)
|
||||
{:ok, _} = CommonAPI.favorite(user2, activity.id)
|
||||
{:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
|
||||
{:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
|
||||
{:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
|
||||
{:ok, reblog_activity1} = CommonAPI.repeat(activity.id, user1)
|
||||
{:ok, _} = CommonAPI.repeat(activity.id, user2)
|
||||
|
||||
conn_res =
|
||||
build_conn()
|
||||
|
@ -917,7 +917,7 @@ test "reblogged status for another user" do
|
|||
test "unreblogs and returns the unreblogged status", %{user: user, conn: conn} do
|
||||
activity = insert(:note_activity)
|
||||
|
||||
{:ok, _, _} = CommonAPI.repeat(activity.id, user)
|
||||
{:ok, _} = CommonAPI.repeat(activity.id, user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|
@ -1427,7 +1427,7 @@ test "requires authentication for private posts", %{user: user} do
|
|||
|
||||
test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
|
||||
other_user = insert(:user)
|
||||
{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
|
||||
{:ok, _} = CommonAPI.repeat(activity.id, other_user)
|
||||
|
||||
response =
|
||||
conn
|
||||
|
@ -1458,7 +1458,7 @@ test "does not return users who have reblogged the status but are blocked", %{
|
|||
other_user = insert(:user)
|
||||
{:ok, _user_relationship} = User.block(user, other_user)
|
||||
|
||||
{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
|
||||
{:ok, _} = CommonAPI.repeat(activity.id, other_user)
|
||||
|
||||
response =
|
||||
conn
|
||||
|
@ -1469,12 +1469,12 @@ test "does not return users who have reblogged the status but are blocked", %{
|
|||
end
|
||||
|
||||
test "does not return users who have reblogged the status privately", %{
|
||||
conn: conn,
|
||||
activity: activity
|
||||
conn: conn
|
||||
} do
|
||||
other_user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(other_user, %{status: "my secret post"})
|
||||
|
||||
{:ok, _, _} = CommonAPI.repeat(activity.id, other_user, %{visibility: "private"})
|
||||
{:ok, _} = CommonAPI.repeat(activity.id, other_user, %{visibility: "private"})
|
||||
|
||||
response =
|
||||
conn
|
||||
|
@ -1486,7 +1486,7 @@ test "does not return users who have reblogged the status privately", %{
|
|||
|
||||
test "does not fail on an unauthenticated request", %{activity: activity} do
|
||||
other_user = insert(:user)
|
||||
{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
|
||||
{:ok, _} = CommonAPI.repeat(activity.id, other_user)
|
||||
|
||||
response =
|
||||
build_conn()
|
||||
|
|
|
@ -78,7 +78,7 @@ test "Reblog notification" do
|
|||
user = insert(:user)
|
||||
another_user = insert(:user)
|
||||
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
|
||||
{:ok, reblog_activity, _object} = CommonAPI.repeat(create_activity.id, another_user)
|
||||
{:ok, reblog_activity} = CommonAPI.repeat(create_activity.id, another_user)
|
||||
{:ok, [notification]} = Notification.create_notifications(reblog_activity)
|
||||
reblog_activity = Activity.get_by_id(create_activity.id)
|
||||
|
||||
|
|
|
@ -442,7 +442,7 @@ test "a reblog" do
|
|||
user = insert(:user)
|
||||
activity = insert(:note_activity)
|
||||
|
||||
{:ok, reblog, _} = CommonAPI.repeat(activity.id, user)
|
||||
{:ok, reblog} = CommonAPI.repeat(activity.id, user)
|
||||
|
||||
represented = StatusView.render("show.json", %{for: user, activity: reblog})
|
||||
|
||||
|
@ -600,7 +600,7 @@ test "does not embed a relationship in the account in reposts" do
|
|||
status: "˙˙ɐʎns"
|
||||
})
|
||||
|
||||
{:ok, activity, _object} = CommonAPI.repeat(activity.id, other_user)
|
||||
{:ok, activity} = CommonAPI.repeat(activity.id, other_user)
|
||||
|
||||
result = StatusView.render("show.json", %{activity: activity, for: user})
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ test "renders title and body for announce activity" do
|
|||
"<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
|
||||
})
|
||||
|
||||
{:ok, announce_activity, _} = CommonAPI.repeat(activity.id, user)
|
||||
{:ok, announce_activity} = CommonAPI.repeat(activity.id, user)
|
||||
object = Object.normalize(activity)
|
||||
|
||||
assert Impl.format_body(%{activity: announce_activity}, user, object) ==
|
||||
|
|
|
@ -106,7 +106,7 @@ test "it streams boosts of the user in the 'user' stream", %{user: user} do
|
|||
|
||||
other_user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
|
||||
{:ok, announce, _} = CommonAPI.repeat(activity.id, user)
|
||||
{:ok, announce} = CommonAPI.repeat(activity.id, user)
|
||||
|
||||
assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce}
|
||||
refute Streamer.filtered_by_user?(user, announce)
|
||||
|
@ -427,7 +427,7 @@ test "it filters muted reblogs" do
|
|||
{:ok, create_activity} = CommonAPI.post(user3, %{status: "I'm kawen"})
|
||||
|
||||
Streamer.get_topic_and_add_socket("user", user1)
|
||||
{:ok, announce_activity, _} = CommonAPI.repeat(create_activity.id, user2)
|
||||
{:ok, announce_activity} = CommonAPI.repeat(create_activity.id, user2)
|
||||
assert_receive {:render_with_user, _, _, ^announce_activity}
|
||||
assert Streamer.filtered_by_user?(user1, announce_activity)
|
||||
end
|
||||
|
@ -440,7 +440,7 @@ test "it filters reblog notification for reblog-muted actors" do
|
|||
|
||||
{:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"})
|
||||
Streamer.get_topic_and_add_socket("user", user1)
|
||||
{:ok, _favorite_activity, _} = CommonAPI.repeat(create_activity.id, user2)
|
||||
{:ok, _announce_activity} = CommonAPI.repeat(create_activity.id, user2)
|
||||
|
||||
assert_receive {:render_with_user, _, "notification.json", notif}
|
||||
assert Streamer.filtered_by_user?(user1, notif)
|
||||
|
|
Loading…
Reference in a new issue