forked from AkkomaGang/akkoma
Merge branch 'features/validators-note' into 'develop'
Pipeline Ingestion: Note Closes #290 See merge request pleroma/pleroma!2984
This commit is contained in:
commit
c4b4258374
31 changed files with 389 additions and 318 deletions
|
@ -13,21 +13,33 @@ def cast(object) when is_binary(object) do
|
||||||
cast([object])
|
cast([object])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cast(object) when is_map(object) do
|
||||||
|
case ObjectID.cast(object) do
|
||||||
|
{:ok, data} -> {:ok, [data]}
|
||||||
|
_ -> :error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def cast(data) when is_list(data) do
|
def cast(data) when is_list(data) do
|
||||||
|
data =
|
||||||
data
|
data
|
||||||
|> Enum.reduce_while({:ok, []}, fn element, {:ok, list} ->
|
|> Enum.reduce_while([], fn element, list ->
|
||||||
case ObjectID.cast(element) do
|
case ObjectID.cast(element) do
|
||||||
{:ok, id} ->
|
{:ok, id} ->
|
||||||
{:cont, {:ok, [id | list]}}
|
{:cont, [id | list]}
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
{:halt, :error}
|
{:cont, list}
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|> Enum.sort()
|
||||||
|
|> Enum.uniq()
|
||||||
|
|
||||||
|
{:ok, data}
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast(_) do
|
def cast(data) do
|
||||||
:error
|
{:error, data}
|
||||||
end
|
end
|
||||||
|
|
||||||
def dump(data) do
|
def dump(data) do
|
||||||
|
|
|
@ -12,4 +12,10 @@ def put_if_present(map, key, value, value_function \\ &{:ok, &1}) when is_map(ma
|
||||||
_ -> map
|
_ -> map
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def safe_put_in(data, keys, value) when is_map(data) and is_list(keys) do
|
||||||
|
Kernel.put_in(data, keys, value)
|
||||||
|
rescue
|
||||||
|
_ -> data
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Object.Fetcher do
|
defmodule Pleroma.Object.Fetcher do
|
||||||
alias Pleroma.HTTP
|
alias Pleroma.HTTP
|
||||||
|
alias Pleroma.Maps
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Object.Containment
|
alias Pleroma.Object.Containment
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
@ -101,6 +102,9 @@ def fetch_object_from_id(id, options \\ []) do
|
||||||
{:transmogrifier, {:error, {:reject, e}}} ->
|
{:transmogrifier, {:error, {:reject, e}}} ->
|
||||||
{:reject, e}
|
{:reject, e}
|
||||||
|
|
||||||
|
{:transmogrifier, {:reject, e}} ->
|
||||||
|
{:reject, e}
|
||||||
|
|
||||||
{:transmogrifier, _} = e ->
|
{:transmogrifier, _} = e ->
|
||||||
{:error, e}
|
{:error, e}
|
||||||
|
|
||||||
|
@ -124,12 +128,14 @@ def fetch_object_from_id(id, options \\ []) do
|
||||||
defp prepare_activity_params(data) do
|
defp prepare_activity_params(data) do
|
||||||
%{
|
%{
|
||||||
"type" => "Create",
|
"type" => "Create",
|
||||||
"to" => data["to"] || [],
|
|
||||||
"cc" => data["cc"] || [],
|
|
||||||
# Should we seriously keep this attributedTo thing?
|
# Should we seriously keep this attributedTo thing?
|
||||||
"actor" => data["actor"] || data["attributedTo"],
|
"actor" => data["actor"] || data["attributedTo"],
|
||||||
"object" => data
|
"object" => data
|
||||||
}
|
}
|
||||||
|
|> Maps.put_if_present("to", data["to"])
|
||||||
|
|> Maps.put_if_present("cc", data["cc"])
|
||||||
|
|> Maps.put_if_present("bto", data["bto"])
|
||||||
|
|> Maps.put_if_present("bcc", data["bcc"])
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_object_from_id!(id, options \\ []) do
|
def fetch_object_from_id!(id, options \\ []) do
|
||||||
|
|
|
@ -88,7 +88,7 @@ defp increase_replies_count_if_reply(%{
|
||||||
|
|
||||||
defp increase_replies_count_if_reply(_create_data), do: :noop
|
defp increase_replies_count_if_reply(_create_data), do: :noop
|
||||||
|
|
||||||
@object_types ~w[ChatMessage Question Answer Audio Video Event Article]
|
@object_types ~w[ChatMessage Question Answer Audio Video Event Article Note]
|
||||||
@impl true
|
@impl true
|
||||||
def persist(%{"type" => type} = object, meta) when type in @object_types do
|
def persist(%{"type" => type} = object, meta) when type in @object_types do
|
||||||
with {:ok, object} <- Object.create(object) do
|
with {:ok, object} <- Object.create(object) do
|
||||||
|
|
|
@ -102,7 +102,7 @@ def validate(
|
||||||
%{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
|
%{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
|
||||||
meta
|
meta
|
||||||
)
|
)
|
||||||
when objtype in ~w[Question Answer Audio Video Event Article] do
|
when objtype in ~w[Question Answer Audio Video Event Article Note] do
|
||||||
with {:ok, object_data} <- cast_and_apply(object),
|
with {:ok, object_data} <- cast_and_apply(object),
|
||||||
meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),
|
meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),
|
||||||
{:ok, create_activity} <-
|
{:ok, create_activity} <-
|
||||||
|
@ -115,7 +115,7 @@ def validate(
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate(%{"type" => type} = object, meta)
|
def validate(%{"type" => type} = object, meta)
|
||||||
when type in ~w[Event Question Audio Video Article] do
|
when type in ~w[Event Question Audio Video Article Note] do
|
||||||
validator =
|
validator =
|
||||||
case type do
|
case type do
|
||||||
"Event" -> EventValidator
|
"Event" -> EventValidator
|
||||||
|
@ -123,6 +123,7 @@ def validate(%{"type" => type} = object, meta)
|
||||||
"Audio" -> AudioVideoValidator
|
"Audio" -> AudioVideoValidator
|
||||||
"Video" -> AudioVideoValidator
|
"Video" -> AudioVideoValidator
|
||||||
"Article" -> ArticleNoteValidator
|
"Article" -> ArticleNoteValidator
|
||||||
|
"Note" -> ArticleNoteValidator
|
||||||
end
|
end
|
||||||
|
|
||||||
with {:ok, object} <-
|
with {:ok, object} <-
|
||||||
|
@ -194,7 +195,7 @@ def cast_and_apply(%{"type" => "Event"} = object) do
|
||||||
EventValidator.cast_and_apply(object)
|
EventValidator.cast_and_apply(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast_and_apply(%{"type" => "Article"} = object) do
|
def cast_and_apply(%{"type" => type} = object) when type in ~w[Article Note] do
|
||||||
ArticleNoteValidator.cast_and_apply(object)
|
ArticleNoteValidator.cast_and_apply(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
@ -23,6 +24,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator do
|
||||||
field(:name, :string)
|
field(:name, :string)
|
||||||
field(:inReplyTo, ObjectValidators.ObjectID)
|
field(:inReplyTo, ObjectValidators.ObjectID)
|
||||||
field(:attributedTo, ObjectValidators.ObjectID)
|
field(:attributedTo, ObjectValidators.ObjectID)
|
||||||
|
field(:context, :string)
|
||||||
|
|
||||||
# TODO: Remove actor on objects
|
# TODO: Remove actor on objects
|
||||||
field(:actor, ObjectValidators.ObjectID)
|
field(:actor, ObjectValidators.ObjectID)
|
||||||
|
@ -46,6 +48,11 @@ def cast_data(data) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def changeset(struct, data) do
|
def changeset(struct, data) do
|
||||||
|
data =
|
||||||
|
data
|
||||||
|
|> CommonFixes.fix_actor()
|
||||||
|
|> CommonFixes.fix_object_defaults()
|
||||||
|
|
||||||
struct
|
struct
|
||||||
|> cast(data, __schema__(:fields))
|
|> cast(data, __schema__(:fields))
|
||||||
end
|
end
|
||||||
|
|
|
@ -50,6 +50,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator do
|
||||||
|
|
||||||
field(:likes, {:array, ObjectValidators.ObjectID}, default: [])
|
field(:likes, {:array, ObjectValidators.ObjectID}, default: [])
|
||||||
field(:announcements, {:array, ObjectValidators.ObjectID}, default: [])
|
field(:announcements, {:array, ObjectValidators.ObjectID}, default: [])
|
||||||
|
|
||||||
|
field(:replies, {:array, ObjectValidators.ObjectID}, default: [])
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast_and_apply(data) do
|
def cast_and_apply(data) do
|
||||||
|
@ -65,25 +67,39 @@ def cast_and_validate(data) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast_data(data) do
|
def cast_data(data) do
|
||||||
data = fix(data)
|
|
||||||
|
|
||||||
%__MODULE__{}
|
%__MODULE__{}
|
||||||
|> changeset(data)
|
|> changeset(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fix_url(%{"url" => url} = data) when is_map(url) do
|
defp fix_url(%{"url" => url} = data) when is_bitstring(url), do: data
|
||||||
Map.put(data, "url", url["href"])
|
defp fix_url(%{"url" => url} = data) when is_map(url), do: Map.put(data, "url", url["href"])
|
||||||
end
|
|
||||||
|
|
||||||
defp fix_url(data), do: data
|
defp fix_url(data), do: data
|
||||||
|
|
||||||
|
defp fix_tag(%{"tag" => tag} = data) when is_list(tag), do: data
|
||||||
|
defp fix_tag(%{"tag" => tag} = data) when is_map(tag), do: Map.put(data, "tag", [tag])
|
||||||
|
defp fix_tag(data), do: Map.drop(data, ["tag"])
|
||||||
|
|
||||||
|
defp fix_replies(%{"replies" => %{"first" => %{"items" => replies}}} = data)
|
||||||
|
when is_list(replies),
|
||||||
|
do: Map.put(data, "replies", replies)
|
||||||
|
|
||||||
|
defp fix_replies(%{"replies" => %{"items" => replies}} = data) when is_list(replies),
|
||||||
|
do: Map.put(data, "replies", replies)
|
||||||
|
|
||||||
|
defp fix_replies(%{"replies" => replies} = data) when is_bitstring(replies),
|
||||||
|
do: Map.drop(data, ["replies"])
|
||||||
|
|
||||||
|
defp fix_replies(data), do: data
|
||||||
|
|
||||||
defp fix(data) do
|
defp fix(data) do
|
||||||
data
|
data
|
||||||
|> CommonFixes.fix_defaults()
|
|
||||||
|> CommonFixes.fix_attribution()
|
|
||||||
|> CommonFixes.fix_actor()
|
|> CommonFixes.fix_actor()
|
||||||
|
|> CommonFixes.fix_object_defaults()
|
||||||
|> fix_url()
|
|> fix_url()
|
||||||
|
|> fix_tag()
|
||||||
|
|> fix_replies()
|
||||||
|> Transmogrifier.fix_emoji()
|
|> Transmogrifier.fix_emoji()
|
||||||
|
|> Transmogrifier.fix_content_map()
|
||||||
end
|
end
|
||||||
|
|
||||||
def changeset(struct, data) do
|
def changeset(struct, data) do
|
||||||
|
|
|
@ -119,9 +119,8 @@ defp fix_content(data), do: data
|
||||||
|
|
||||||
defp fix(data) do
|
defp fix(data) do
|
||||||
data
|
data
|
||||||
|> CommonFixes.fix_defaults()
|
|
||||||
|> CommonFixes.fix_attribution()
|
|
||||||
|> CommonFixes.fix_actor()
|
|> CommonFixes.fix_actor()
|
||||||
|
|> CommonFixes.fix_object_defaults()
|
||||||
|> Transmogrifier.fix_emoji()
|
|> Transmogrifier.fix_emoji()
|
||||||
|> fix_url()
|
|> fix_url()
|
||||||
|> fix_content()
|
|> fix_content()
|
||||||
|
|
|
@ -3,26 +3,55 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
||||||
|
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||||
alias Pleroma.Object.Containment
|
alias Pleroma.Object.Containment
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
|
||||||
# based on Pleroma.Web.ActivityPub.Utils.lazy_put_objects_defaults
|
def cast_and_filter_recipients(message, field, follower_collection, field_fallback \\ []) do
|
||||||
def fix_defaults(data) do
|
{:ok, data} = ObjectValidators.Recipients.cast(message[field] || field_fallback)
|
||||||
|
|
||||||
|
data =
|
||||||
|
Enum.reject(data, fn x ->
|
||||||
|
String.ends_with?(x, "/followers") and x != follower_collection
|
||||||
|
end)
|
||||||
|
|
||||||
|
Map.put(message, field, data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def fix_object_defaults(data) do
|
||||||
%{data: %{"id" => context}, id: context_id} =
|
%{data: %{"id" => context}, id: context_id} =
|
||||||
Utils.create_context(data["context"] || data["conversation"])
|
Utils.create_context(data["context"] || data["conversation"])
|
||||||
|
|
||||||
|
%User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["attributedTo"])
|
||||||
|
|
||||||
data
|
data
|
||||||
|> Map.put("context", context)
|
|> Map.put("context", context)
|
||||||
|> Map.put("context_id", context_id)
|
|> Map.put("context_id", context_id)
|
||||||
|
|> cast_and_filter_recipients("to", follower_collection)
|
||||||
|
|> cast_and_filter_recipients("cc", follower_collection)
|
||||||
|
|> cast_and_filter_recipients("bto", follower_collection)
|
||||||
|
|> cast_and_filter_recipients("bcc", follower_collection)
|
||||||
|
|> Transmogrifier.fix_implicit_addressing(follower_collection)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_attribution(data) do
|
def fix_activity_addressing(activity, _meta) do
|
||||||
data
|
%User{follower_address: follower_collection} = User.get_cached_by_ap_id(activity["actor"])
|
||||||
|> Map.put_new("actor", data["attributedTo"])
|
|
||||||
|
activity
|
||||||
|
|> cast_and_filter_recipients("to", follower_collection)
|
||||||
|
|> cast_and_filter_recipients("cc", follower_collection)
|
||||||
|
|> cast_and_filter_recipients("bto", follower_collection)
|
||||||
|
|> cast_and_filter_recipients("bcc", follower_collection)
|
||||||
|
|> Transmogrifier.fix_implicit_addressing(follower_collection)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_actor(data) do
|
def fix_actor(data) do
|
||||||
actor = Containment.get_actor(data)
|
actor =
|
||||||
|
data
|
||||||
|
|> Map.put_new("actor", data["attributedTo"])
|
||||||
|
|> Containment.get_actor()
|
||||||
|
|
||||||
data
|
data
|
||||||
|> Map.put("actor", actor)
|
|> Map.put("actor", actor)
|
||||||
|
|
|
@ -15,6 +15,7 @@ def validate_any_presence(cng, fields) do
|
||||||
fields
|
fields
|
||||||
|> Enum.map(fn field -> get_field(cng, field) end)
|
|> Enum.map(fn field -> get_field(cng, field) end)
|
||||||
|> Enum.any?(fn
|
|> Enum.any?(fn
|
||||||
|
nil -> false
|
||||||
[] -> false
|
[] -> false
|
||||||
_ -> true
|
_ -> true
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -10,8 +10,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do
|
||||||
|
|
||||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||||
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
@ -23,6 +25,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do
|
||||||
field(:type, :string)
|
field(:type, :string)
|
||||||
field(:to, ObjectValidators.Recipients, default: [])
|
field(:to, ObjectValidators.Recipients, default: [])
|
||||||
field(:cc, ObjectValidators.Recipients, default: [])
|
field(:cc, ObjectValidators.Recipients, default: [])
|
||||||
|
field(:bto, ObjectValidators.Recipients, default: [])
|
||||||
|
field(:bcc, ObjectValidators.Recipients, default: [])
|
||||||
field(:object, ObjectValidators.ObjectID)
|
field(:object, ObjectValidators.ObjectID)
|
||||||
field(:expires_at, ObjectValidators.DateTime)
|
field(:expires_at, ObjectValidators.DateTime)
|
||||||
|
|
||||||
|
@ -54,39 +58,37 @@ def changeset(struct, data) do
|
||||||
|> cast(data, __schema__(:fields))
|
|> cast(data, __schema__(:fields))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fix_context(data, meta) do
|
# CommonFixes.fix_activity_addressing adapted for Create specific behavior
|
||||||
if object = meta[:object_data] do
|
defp fix_addressing(data, object) do
|
||||||
Map.put_new(data, "context", object["context"])
|
%User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["actor"])
|
||||||
else
|
|
||||||
data
|
data
|
||||||
end
|
|> CommonFixes.cast_and_filter_recipients("to", follower_collection, object["to"])
|
||||||
|
|> CommonFixes.cast_and_filter_recipients("cc", follower_collection, object["cc"])
|
||||||
|
|> CommonFixes.cast_and_filter_recipients("bto", follower_collection, object["bto"])
|
||||||
|
|> CommonFixes.cast_and_filter_recipients("bcc", follower_collection, object["bcc"])
|
||||||
|
|> Transmogrifier.fix_implicit_addressing(follower_collection)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fix_addressing(data, meta) do
|
def fix(data, meta) do
|
||||||
if object = meta[:object_data] do
|
object = meta[:object_data]
|
||||||
data
|
|
||||||
|> Map.put_new("to", object["to"] || [])
|
|
||||||
|> Map.put_new("cc", object["cc"] || [])
|
|
||||||
else
|
|
||||||
data
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp fix(data, meta) do
|
|
||||||
data
|
data
|
||||||
|> fix_context(meta)
|
|
||||||
|> fix_addressing(meta)
|
|
||||||
|> CommonFixes.fix_actor()
|
|> CommonFixes.fix_actor()
|
||||||
|
|> Map.put_new("context", object["context"])
|
||||||
|
|> fix_addressing(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp validate_data(cng, meta) do
|
defp validate_data(cng, meta) do
|
||||||
|
object = meta[:object_data]
|
||||||
|
|
||||||
cng
|
cng
|
||||||
|> validate_required([:actor, :type, :object])
|
|> validate_required([:actor, :type, :object, :to, :cc])
|
||||||
|> validate_inclusion(:type, ["Create"])
|
|> validate_inclusion(:type, ["Create"])
|
||||||
|> CommonValidations.validate_actor_presence()
|
|> CommonValidations.validate_actor_presence()
|
||||||
|> CommonValidations.validate_any_presence([:to, :cc])
|
|> validate_actors_match(object)
|
||||||
|> validate_actors_match(meta)
|
|> validate_context_match(object)
|
||||||
|> validate_context_match(meta)
|
|> validate_addressing_match(object)
|
||||||
|> validate_object_nonexistence()
|
|> validate_object_nonexistence()
|
||||||
|> validate_object_containment()
|
|> validate_object_containment()
|
||||||
end
|
end
|
||||||
|
@ -118,8 +120,8 @@ def validate_object_nonexistence(cng) do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_actors_match(cng, meta) do
|
def validate_actors_match(cng, object) do
|
||||||
attributed_to = meta[:object_data]["attributedTo"] || meta[:object_data]["actor"]
|
attributed_to = object["attributedTo"] || object["actor"]
|
||||||
|
|
||||||
cng
|
cng
|
||||||
|> validate_change(:actor, fn :actor, actor ->
|
|> validate_change(:actor, fn :actor, actor ->
|
||||||
|
@ -131,7 +133,7 @@ def validate_actors_match(cng, meta) do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_context_match(cng, %{object_data: %{"context" => object_context}}) do
|
def validate_context_match(cng, %{"context" => object_context}) do
|
||||||
cng
|
cng
|
||||||
|> validate_change(:context, fn :context, context ->
|
|> validate_change(:context, fn :context, context ->
|
||||||
if context == object_context do
|
if context == object_context do
|
||||||
|
@ -142,5 +144,18 @@ def validate_context_match(cng, %{object_data: %{"context" => object_context}})
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_context_match(cng, _), do: cng
|
def validate_addressing_match(cng, object) do
|
||||||
|
[:to, :cc, :bcc, :bto]
|
||||||
|
|> Enum.reduce(cng, fn field, cng ->
|
||||||
|
object_data = object[to_string(field)]
|
||||||
|
|
||||||
|
validate_change(cng, field, fn field, data ->
|
||||||
|
if data == object_data do
|
||||||
|
[]
|
||||||
|
else
|
||||||
|
[{field, "field doesn't match with object (#{inspect(object_data)})"}]
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
# Pleroma: A lightweight social networking server
|
|
||||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateNoteValidator do
|
|
||||||
use Ecto.Schema
|
|
||||||
|
|
||||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator
|
|
||||||
|
|
||||||
import Ecto.Changeset
|
|
||||||
|
|
||||||
@primary_key false
|
|
||||||
|
|
||||||
embedded_schema do
|
|
||||||
field(:id, ObjectValidators.ObjectID, primary_key: true)
|
|
||||||
field(:actor, ObjectValidators.ObjectID)
|
|
||||||
field(:type, :string)
|
|
||||||
field(:to, ObjectValidators.Recipients, default: [])
|
|
||||||
field(:cc, ObjectValidators.Recipients, default: [])
|
|
||||||
field(:bto, ObjectValidators.Recipients, default: [])
|
|
||||||
field(:bcc, ObjectValidators.Recipients, default: [])
|
|
||||||
embeds_one(:object, NoteValidator)
|
|
||||||
end
|
|
||||||
|
|
||||||
def cast_data(data) do
|
|
||||||
cast(%__MODULE__{}, data, __schema__(:fields))
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -72,8 +72,8 @@ def cast_data(data) do
|
||||||
|
|
||||||
defp fix(data) do
|
defp fix(data) do
|
||||||
data
|
data
|
||||||
|> CommonFixes.fix_defaults()
|
|> CommonFixes.fix_actor()
|
||||||
|> CommonFixes.fix_attribution()
|
|> CommonFixes.fix_object_defaults()
|
||||||
|> Transmogrifier.fix_emoji()
|
|> Transmogrifier.fix_emoji()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -83,8 +83,8 @@ defp fix_closed(data) do
|
||||||
|
|
||||||
defp fix(data) do
|
defp fix(data) do
|
||||||
data
|
data
|
||||||
|> CommonFixes.fix_defaults()
|
|> CommonFixes.fix_actor()
|
||||||
|> CommonFixes.fix_attribution()
|
|> CommonFixes.fix_object_defaults()
|
||||||
|> Transmogrifier.fix_emoji()
|
|> Transmogrifier.fix_emoji()
|
||||||
|> fix_closed()
|
|> fix_closed()
|
||||||
end
|
end
|
||||||
|
|
|
@ -203,6 +203,19 @@ def handle(%{data: %{"type" => "Create"}} = activity, meta) do
|
||||||
Object.increase_replies_count(in_reply_to)
|
Object.increase_replies_count(in_reply_to)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
reply_depth = (meta[:depth] || 0) + 1
|
||||||
|
|
||||||
|
# FIXME: Force inReplyTo to replies
|
||||||
|
if Pleroma.Web.Federator.allowed_thread_distance?(reply_depth) and
|
||||||
|
object.data["replies"] != nil do
|
||||||
|
for reply_id <- object.data["replies"] do
|
||||||
|
Pleroma.Workers.RemoteFetcherWorker.enqueue("fetch_remote", %{
|
||||||
|
"id" => reply_id,
|
||||||
|
"depth" => reply_depth
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
ConcurrentLimiter.limit(Pleroma.Web.RichMedia.Helpers, fn ->
|
ConcurrentLimiter.limit(Pleroma.Web.RichMedia.Helpers, fn ->
|
||||||
Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end)
|
Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end)
|
||||||
end)
|
end)
|
||||||
|
@ -423,7 +436,7 @@ def handle_object_creation(%{"type" => "Answer"} = object_map, meta) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_object_creation(%{"type" => objtype} = object, meta)
|
def handle_object_creation(%{"type" => objtype} = object, meta)
|
||||||
when objtype in ~w[Audio Video Question Event Article] do
|
when objtype in ~w[Audio Video Question Event Article Note] do
|
||||||
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
|
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
|
||||||
{:ok, object, meta}
|
{:ok, object, meta}
|
||||||
end
|
end
|
||||||
|
|
|
@ -43,7 +43,6 @@ def fix_object(object, options \\ []) do
|
||||||
|> fix_content_map()
|
|> fix_content_map()
|
||||||
|> fix_addressing()
|
|> fix_addressing()
|
||||||
|> fix_summary()
|
|> fix_summary()
|
||||||
|> fix_type(options)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_summary(%{"summary" => nil} = object) do
|
def fix_summary(%{"summary" => nil} = object) do
|
||||||
|
@ -72,17 +71,21 @@ def fix_addressing_list(map, field) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_explicit_addressing(
|
# if directMessage flag is set to true, leave the addressing alone
|
||||||
%{"to" => to, "cc" => cc} = object,
|
def fix_explicit_addressing(%{"directMessage" => true} = object, _follower_collection),
|
||||||
explicit_mentions,
|
do: object
|
||||||
follower_collection
|
|
||||||
) do
|
|
||||||
explicit_to = Enum.filter(to, fn x -> x in explicit_mentions end)
|
|
||||||
|
|
||||||
|
def fix_explicit_addressing(%{"to" => to, "cc" => cc} = object, follower_collection) do
|
||||||
|
explicit_mentions =
|
||||||
|
Utils.determine_explicit_mentions(object) ++
|
||||||
|
[Pleroma.Constants.as_public(), follower_collection]
|
||||||
|
|
||||||
|
explicit_to = Enum.filter(to, fn x -> x in explicit_mentions end)
|
||||||
explicit_cc = Enum.filter(to, fn x -> x not in explicit_mentions end)
|
explicit_cc = Enum.filter(to, fn x -> x not in explicit_mentions end)
|
||||||
|
|
||||||
final_cc =
|
final_cc =
|
||||||
(cc ++ explicit_cc)
|
(cc ++ explicit_cc)
|
||||||
|
|> Enum.filter(& &1)
|
||||||
|> Enum.reject(fn x -> String.ends_with?(x, "/followers") and x != follower_collection end)
|
|> Enum.reject(fn x -> String.ends_with?(x, "/followers") and x != follower_collection end)
|
||||||
|> Enum.uniq()
|
|> Enum.uniq()
|
||||||
|
|
||||||
|
@ -91,29 +94,6 @@ def fix_explicit_addressing(
|
||||||
|> Map.put("cc", final_cc)
|
|> Map.put("cc", final_cc)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_explicit_addressing(object, _explicit_mentions, _followers_collection), do: object
|
|
||||||
|
|
||||||
# if directMessage flag is set to true, leave the addressing alone
|
|
||||||
def fix_explicit_addressing(%{"directMessage" => true} = object), do: object
|
|
||||||
|
|
||||||
def fix_explicit_addressing(object) do
|
|
||||||
explicit_mentions = Utils.determine_explicit_mentions(object)
|
|
||||||
|
|
||||||
%User{follower_address: follower_collection} =
|
|
||||||
object
|
|
||||||
|> Containment.get_actor()
|
|
||||||
|> User.get_cached_by_ap_id()
|
|
||||||
|
|
||||||
explicit_mentions =
|
|
||||||
explicit_mentions ++
|
|
||||||
[
|
|
||||||
Pleroma.Constants.as_public(),
|
|
||||||
follower_collection
|
|
||||||
]
|
|
||||||
|
|
||||||
fix_explicit_addressing(object, explicit_mentions, follower_collection)
|
|
||||||
end
|
|
||||||
|
|
||||||
# if as:Public is addressed, then make sure the followers collection is also addressed
|
# if as:Public is addressed, then make sure the followers collection is also addressed
|
||||||
# so that the activities will be delivered to local users.
|
# so that the activities will be delivered to local users.
|
||||||
def fix_implicit_addressing(%{"to" => to, "cc" => cc} = object, followers_collection) do
|
def fix_implicit_addressing(%{"to" => to, "cc" => cc} = object, followers_collection) do
|
||||||
|
@ -137,19 +117,19 @@ def fix_implicit_addressing(%{"to" => to, "cc" => cc} = object, followers_collec
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_implicit_addressing(object, _), do: object
|
|
||||||
|
|
||||||
def fix_addressing(object) do
|
def fix_addressing(object) do
|
||||||
{:ok, %User{} = user} = User.get_or_fetch_by_ap_id(object["actor"])
|
{:ok, %User{follower_address: follower_collection}} =
|
||||||
followers_collection = User.ap_followers(user)
|
object
|
||||||
|
|> Containment.get_actor()
|
||||||
|
|> User.get_or_fetch_by_ap_id()
|
||||||
|
|
||||||
object
|
object
|
||||||
|> fix_addressing_list("to")
|
|> fix_addressing_list("to")
|
||||||
|> fix_addressing_list("cc")
|
|> fix_addressing_list("cc")
|
||||||
|> fix_addressing_list("bto")
|
|> fix_addressing_list("bto")
|
||||||
|> fix_addressing_list("bcc")
|
|> fix_addressing_list("bcc")
|
||||||
|> fix_explicit_addressing()
|
|> fix_explicit_addressing(follower_collection)
|
||||||
|> fix_implicit_addressing(followers_collection)
|
|> fix_implicit_addressing(follower_collection)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_actor(%{"attributedTo" => actor} = object) do
|
def fix_actor(%{"attributedTo" => actor} = object) do
|
||||||
|
@ -342,19 +322,18 @@ def fix_content_map(%{"contentMap" => content_map} = object) do
|
||||||
|
|
||||||
def fix_content_map(object), do: object
|
def fix_content_map(object), do: object
|
||||||
|
|
||||||
def fix_type(object, options \\ [])
|
defp fix_type(%{"type" => "Note", "inReplyTo" => reply_id, "name" => _} = object, options)
|
||||||
|
|
||||||
def fix_type(%{"inReplyTo" => reply_id, "name" => _} = object, options)
|
|
||||||
when is_binary(reply_id) do
|
when is_binary(reply_id) do
|
||||||
with true <- Federator.allowed_thread_distance?(options[:depth]),
|
options = Keyword.put(options, :fetch, true)
|
||||||
{:ok, %{data: %{"type" => "Question"} = _} = _} <- get_obj_helper(reply_id, options) do
|
|
||||||
|
with %Object{data: %{"type" => "Question"}} <- Object.normalize(reply_id, options) do
|
||||||
Map.put(object, "type", "Answer")
|
Map.put(object, "type", "Answer")
|
||||||
else
|
else
|
||||||
_ -> object
|
_ -> object
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_type(object, _), do: object
|
defp fix_type(object, _options), do: object
|
||||||
|
|
||||||
# Reduce the object list to find the reported user.
|
# Reduce the object list to find the reported user.
|
||||||
defp get_reported(objects) do
|
defp get_reported(objects) do
|
||||||
|
@ -425,10 +404,9 @@ def handle_incoming(%{"id" => id}, _options) when is_binary(id) and byte_size(id
|
||||||
# - tags
|
# - tags
|
||||||
# - emoji
|
# - emoji
|
||||||
def handle_incoming(
|
def handle_incoming(
|
||||||
%{"type" => "Create", "object" => %{"type" => objtype} = object} = data,
|
%{"type" => "Create", "object" => %{"type" => "Page"} = object} = data,
|
||||||
options
|
options
|
||||||
)
|
) do
|
||||||
when objtype in ~w{Note Page} do
|
|
||||||
actor = Containment.get_actor(data)
|
actor = Containment.get_actor(data)
|
||||||
|
|
||||||
with nil <- Activity.get_create_by_object_ap_id(object["id"]),
|
with nil <- Activity.get_create_by_object_ap_id(object["id"]),
|
||||||
|
@ -520,14 +498,23 @@ def handle_incoming(
|
||||||
|
|
||||||
def handle_incoming(
|
def handle_incoming(
|
||||||
%{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data,
|
%{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data,
|
||||||
_options
|
options
|
||||||
)
|
)
|
||||||
when objtype in ~w{Question Answer ChatMessage Audio Video Event Article} do
|
when objtype in ~w{Question Answer ChatMessage Audio Video Event Article Note} do
|
||||||
data = Map.put(data, "object", strip_internal_fields(data["object"]))
|
fetch_options = Keyword.put(options, :depth, (options[:depth] || 0) + 1)
|
||||||
|
|
||||||
|
object =
|
||||||
|
data["object"]
|
||||||
|
|> strip_internal_fields()
|
||||||
|
|> fix_type(fetch_options)
|
||||||
|
|> fix_in_reply_to(fetch_options)
|
||||||
|
|
||||||
|
data = Map.put(data, "object", object)
|
||||||
|
options = Keyword.put(options, :local, false)
|
||||||
|
|
||||||
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
|
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
|
||||||
nil <- Activity.get_create_by_object_ap_id(obj_id),
|
nil <- Activity.get_create_by_object_ap_id(obj_id),
|
||||||
{:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
|
{:ok, activity, _} <- Pipeline.common_pipeline(data, options) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
%Activity{} = activity -> {:ok, activity}
|
%Activity{} = activity -> {:ok, activity}
|
||||||
|
|
|
@ -96,8 +96,11 @@ def maybe_splice_recipient(ap_id, params) do
|
||||||
!label_in_collection?(ap_id, params["cc"])
|
!label_in_collection?(ap_id, params["cc"])
|
||||||
|
|
||||||
if need_splice? do
|
if need_splice? do
|
||||||
cc_list = extract_list(params["cc"])
|
cc = [ap_id | extract_list(params["cc"])]
|
||||||
Map.put(params, "cc", [ap_id | cc_list])
|
|
||||||
|
params
|
||||||
|
|> Map.put("cc", cc)
|
||||||
|
|> Maps.safe_put_in(["object", "cc"], cc)
|
||||||
else
|
else
|
||||||
params
|
params
|
||||||
end
|
end
|
||||||
|
|
|
@ -96,6 +96,11 @@ def perform(:incoming_ap_doc, params) do
|
||||||
Logger.debug("Unhandled actor #{actor}, #{inspect(e)}")
|
Logger.debug("Unhandled actor #{actor}, #{inspect(e)}")
|
||||||
{:error, e}
|
{:error, e}
|
||||||
|
|
||||||
|
{:error, {:validate_object, _}} = e ->
|
||||||
|
Logger.error("Incoming AP doc validation error: #{inspect(e)}")
|
||||||
|
Logger.debug(Jason.encode!(params, pretty: true))
|
||||||
|
e
|
||||||
|
|
||||||
e ->
|
e ->
|
||||||
# Just drop those for now
|
# Just drop those for now
|
||||||
Logger.debug(fn -> "Unhandled activity\n" <> Jason.encode!(params, pretty: true) end)
|
Logger.debug(fn -> "Unhandled activity\n" <> Jason.encode!(params, pretty: true) end)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"type": "Create",
|
"type": "Create",
|
||||||
"object": {
|
"object": {
|
||||||
"type": "Note",
|
"type": "Note",
|
||||||
|
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
"content": "It's a note"
|
"content": "It's a note"
|
||||||
},
|
},
|
||||||
"to": ["https://www.w3.org/ns/activitystreams#Public"]
|
"to": ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
|
|
|
@ -123,7 +123,8 @@ test "when association is not loaded" do
|
||||||
"type" => "Note",
|
"type" => "Note",
|
||||||
"content" => "find me!",
|
"content" => "find me!",
|
||||||
"id" => "http://mastodon.example.org/users/admin/objects/1",
|
"id" => "http://mastodon.example.org/users/admin/objects/1",
|
||||||
"attributedTo" => "http://mastodon.example.org/users/admin"
|
"attributedTo" => "http://mastodon.example.org/users/admin",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
},
|
},
|
||||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
}
|
}
|
||||||
|
@ -132,6 +133,7 @@ test "when association is not loaded" do
|
||||||
{:ok, japanese_activity} = Pleroma.Web.CommonAPI.post(user, %{status: "更新情報"})
|
{:ok, japanese_activity} = Pleroma.Web.CommonAPI.post(user, %{status: "更新情報"})
|
||||||
{:ok, job} = Pleroma.Web.Federator.incoming_ap_doc(params)
|
{:ok, job} = Pleroma.Web.Federator.incoming_ap_doc(params)
|
||||||
{:ok, remote_activity} = ObanHelpers.perform(job)
|
{:ok, remote_activity} = ObanHelpers.perform(job)
|
||||||
|
remote_activity = Activity.get_by_id_with_object(remote_activity.id)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
japanese_activity: japanese_activity,
|
japanese_activity: japanese_activity,
|
||||||
|
|
|
@ -6,10 +6,10 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.RecipientsTest do
|
||||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients
|
alias Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients
|
||||||
use Pleroma.DataCase, async: true
|
use Pleroma.DataCase, async: true
|
||||||
|
|
||||||
test "it asserts that all elements of the list are object ids" do
|
test "it only keeps elements that are valid object ids" do
|
||||||
list = ["https://lain.com/users/lain", "invalid"]
|
list = ["https://lain.com/users/lain", "invalid"]
|
||||||
|
|
||||||
assert :error == Recipients.cast(list)
|
assert {:ok, ["https://lain.com/users/lain"]} == Recipients.cast(list)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it works with a list" do
|
test "it works with a list" do
|
||||||
|
|
|
@ -624,6 +624,8 @@ test "it sends notifications to mentioned users in new messages" do
|
||||||
"actor" => user.ap_id,
|
"actor" => user.ap_id,
|
||||||
"object" => %{
|
"object" => %{
|
||||||
"type" => "Note",
|
"type" => "Note",
|
||||||
|
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
"content" => "message with a Mention tag, but no explicit tagging",
|
"content" => "message with a Mention tag, but no explicit tagging",
|
||||||
"tag" => [
|
"tag" => [
|
||||||
%{
|
%{
|
||||||
|
@ -655,6 +657,9 @@ test "it does not send notifications to users who are only cc in new messages" d
|
||||||
"actor" => user.ap_id,
|
"actor" => user.ap_id,
|
||||||
"object" => %{
|
"object" => %{
|
||||||
"type" => "Note",
|
"type" => "Note",
|
||||||
|
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [other_user.ap_id],
|
||||||
"content" => "hi everyone",
|
"content" => "hi everyone",
|
||||||
"attributedTo" => user.ap_id
|
"attributedTo" => user.ap_id
|
||||||
}
|
}
|
||||||
|
@ -951,6 +956,7 @@ test "notifications are deleted if a remote user is deleted" do
|
||||||
"cc" => [],
|
"cc" => [],
|
||||||
"object" => %{
|
"object" => %{
|
||||||
"type" => "Note",
|
"type" => "Note",
|
||||||
|
"id" => remote_user.ap_id <> "/objects/test",
|
||||||
"content" => "Hello!",
|
"content" => "Hello!",
|
||||||
"tag" => [
|
"tag" => [
|
||||||
%{
|
%{
|
||||||
|
|
|
@ -66,6 +66,14 @@ defmodule Pleroma.Object.FetcherTest do
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
status: 500
|
status: 500
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%{
|
||||||
|
method: :get,
|
||||||
|
url: "https://stereophonic.space/objects/02997b83-3ea7-4b63-94af-ef3aa2d4ed17"
|
||||||
|
} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 500
|
||||||
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
|
@ -124,8 +132,7 @@ test "it fetches an object" do
|
||||||
{:ok, object} =
|
{:ok, object} =
|
||||||
Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367")
|
Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367")
|
||||||
|
|
||||||
assert activity = Activity.get_create_by_object_ap_id(object.data["id"])
|
assert _activity = Activity.get_create_by_object_ap_id(object.data["id"])
|
||||||
assert activity.data["id"]
|
|
||||||
|
|
||||||
{:ok, object_again} =
|
{:ok, object_again} =
|
||||||
Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367")
|
Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367")
|
||||||
|
|
|
@ -539,7 +539,7 @@ test "it inserts an incoming activity into the database" <>
|
||||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||||
|> Jason.decode!()
|
|> Jason.decode!()
|
||||||
|> Map.put("actor", user.ap_id)
|
|> Map.put("actor", user.ap_id)
|
||||||
|> put_in(["object", "attridbutedTo"], user.ap_id)
|
|> put_in(["object", "attributedTo"], user.ap_id)
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|
@ -829,7 +829,11 @@ test "mastodon pin/unpin", %{conn: conn} do
|
||||||
|
|
||||||
test "it inserts an incoming activity into the database", %{conn: conn, data: data} do
|
test "it inserts an incoming activity into the database", %{conn: conn, data: data} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
data = Map.put(data, "bcc", [user.ap_id])
|
|
||||||
|
data =
|
||||||
|
data
|
||||||
|
|> Map.put("bcc", [user.ap_id])
|
||||||
|
|> Kernel.put_in(["object", "bcc"], [user.ap_id])
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|
@ -846,8 +850,11 @@ test "it accepts messages with to as string instead of array", %{conn: conn, dat
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
data =
|
data =
|
||||||
Map.put(data, "to", user.ap_id)
|
data
|
||||||
|> Map.delete("cc")
|
|> Map.put("to", user.ap_id)
|
||||||
|
|> Map.put("cc", [])
|
||||||
|
|> Kernel.put_in(["object", "to"], user.ap_id)
|
||||||
|
|> Kernel.put_in(["object", "cc"], [])
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|
@ -864,8 +871,11 @@ test "it accepts messages with cc as string instead of array", %{conn: conn, dat
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
data =
|
data =
|
||||||
Map.put(data, "cc", user.ap_id)
|
data
|
||||||
|> Map.delete("to")
|
|> Map.put("to", [])
|
||||||
|
|> Map.put("cc", user.ap_id)
|
||||||
|
|> Kernel.put_in(["object", "to"], [])
|
||||||
|
|> Kernel.put_in(["object", "cc"], user.ap_id)
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|
@ -883,9 +893,13 @@ test "it accepts messages with bcc as string instead of array", %{conn: conn, da
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
data =
|
data =
|
||||||
Map.put(data, "bcc", user.ap_id)
|
data
|
||||||
|> Map.delete("to")
|
|> Map.put("to", [])
|
||||||
|> Map.delete("cc")
|
|> Map.put("cc", [])
|
||||||
|
|> Map.put("bcc", user.ap_id)
|
||||||
|
|> Kernel.put_in(["object", "to"], [])
|
||||||
|
|> Kernel.put_in(["object", "cc"], [])
|
||||||
|
|> Kernel.put_in(["object", "bcc"], user.ap_id)
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|
@ -1000,29 +1014,34 @@ test "it clears `unreachable` federation status of the sender", %{conn: conn, da
|
||||||
assert Instances.reachable?(sender_host)
|
assert Instances.reachable?(sender_host)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag capture_log: true
|
||||||
test "it removes all follower collections but actor's", %{conn: conn} do
|
test "it removes all follower collections but actor's", %{conn: conn} do
|
||||||
[actor, recipient] = insert_pair(:user)
|
[actor, recipient] = insert_pair(:user)
|
||||||
|
|
||||||
data =
|
to = [
|
||||||
File.read!("test/fixtures/activitypub-client-post-activity.json")
|
|
||||||
|> Jason.decode!()
|
|
||||||
|
|
||||||
object = Map.put(data["object"], "attributedTo", actor.ap_id)
|
|
||||||
|
|
||||||
data =
|
|
||||||
data
|
|
||||||
|> Map.put("id", Utils.generate_object_id())
|
|
||||||
|> Map.put("actor", actor.ap_id)
|
|
||||||
|> Map.put("object", object)
|
|
||||||
|> Map.put("cc", [
|
|
||||||
recipient.follower_address,
|
|
||||||
actor.follower_address
|
|
||||||
])
|
|
||||||
|> Map.put("to", [
|
|
||||||
recipient.ap_id,
|
recipient.ap_id,
|
||||||
recipient.follower_address,
|
recipient.follower_address,
|
||||||
"https://www.w3.org/ns/activitystreams#Public"
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
])
|
]
|
||||||
|
|
||||||
|
cc = [recipient.follower_address, actor.follower_address]
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
"@context" => ["https://www.w3.org/ns/activitystreams"],
|
||||||
|
"type" => "Create",
|
||||||
|
"id" => Utils.generate_activity_id(),
|
||||||
|
"to" => to,
|
||||||
|
"cc" => cc,
|
||||||
|
"actor" => actor.ap_id,
|
||||||
|
"object" => %{
|
||||||
|
"type" => "Note",
|
||||||
|
"to" => to,
|
||||||
|
"cc" => cc,
|
||||||
|
"content" => "It's a note",
|
||||||
|
"attributedTo" => actor.ap_id,
|
||||||
|
"id" => Utils.generate_object_id()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> assign(:valid_signature, true)
|
|> assign(:valid_signature, true)
|
||||||
|
@ -1032,7 +1051,7 @@ test "it removes all follower collections but actor's", %{conn: conn} do
|
||||||
|
|
||||||
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
|
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
|
||||||
|
|
||||||
activity = Activity.get_by_ap_id(data["id"])
|
assert activity = Activity.get_by_ap_id(data["id"])
|
||||||
|
|
||||||
assert activity.id
|
assert activity.id
|
||||||
assert actor.follower_address in activity.recipients
|
assert actor.follower_address in activity.recipients
|
||||||
|
@ -1164,7 +1183,6 @@ test "forwarded report from mastodon", %{conn: conn} do
|
||||||
"actor" => remote_actor,
|
"actor" => remote_actor,
|
||||||
"content" => "test report",
|
"content" => "test report",
|
||||||
"id" => "https://#{remote_domain}/e3b12fd1-948c-446e-b93b-a5e67edbe1d8",
|
"id" => "https://#{remote_domain}/e3b12fd1-948c-446e-b93b-a5e67edbe1d8",
|
||||||
"nickname" => reported_user.nickname,
|
|
||||||
"object" => [
|
"object" => [
|
||||||
reported_user.ap_id,
|
reported_user.ap_id,
|
||||||
note.data["object"]
|
note.data["object"]
|
||||||
|
|
|
@ -24,6 +24,8 @@ test "it works for incoming listens" do
|
||||||
"actor" => "http://mastodon.example.org/users/admin",
|
"actor" => "http://mastodon.example.org/users/admin",
|
||||||
"object" => %{
|
"object" => %{
|
||||||
"type" => "Audio",
|
"type" => "Audio",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
"id" => "http://mastodon.example.org/users/admin/listens/1234",
|
"id" => "http://mastodon.example.org/users/admin/listens/1234",
|
||||||
"attributedTo" => "http://mastodon.example.org/users/admin",
|
"attributedTo" => "http://mastodon.example.org/users/admin",
|
||||||
"title" => "lain radio episode 1",
|
"title" => "lain radio episode 1",
|
||||||
|
@ -61,7 +63,9 @@ test "Funkwhale Audio object" do
|
||||||
|
|
||||||
assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
|
assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
|
|
||||||
assert object.data["cc"] == []
|
assert object.data["cc"] == [
|
||||||
|
"https://channels.tests.funkwhale.audio/federation/actors/compositions/followers"
|
||||||
|
]
|
||||||
|
|
||||||
assert object.data["url"] == "https://channels.tests.funkwhale.audio/library/tracks/74"
|
assert object.data["url"] == "https://channels.tests.funkwhale.audio/library/tracks/74"
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ test "Mobilizon Event object" do
|
||||||
)
|
)
|
||||||
|
|
||||||
assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
|
assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
assert object.data["cc"] == []
|
assert object.data["cc"] == ["https://mobilizon.org/@tcit/followers"]
|
||||||
|
|
||||||
assert object.data["url"] ==
|
assert object.data["url"] ==
|
||||||
"https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
|
"https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
|
||||||
|
|
|
@ -10,11 +10,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
import Mock
|
import Mock
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
import ExUnit.CaptureLog
|
|
||||||
|
|
||||||
setup_all do
|
setup_all do
|
||||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
@ -43,36 +43,6 @@ test "it works for incoming notices with tag not being an array (kroeg)" do
|
||||||
assert Object.hashtags(object) == ["test"]
|
assert Object.hashtags(object) == ["test"]
|
||||||
end
|
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")
|
|
||||||
|> Jason.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, fetch: false).data
|
|
||||||
|
|
||||||
assert object_data["to"] == []
|
|
||||||
assert object_data["cc"] == to
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it ignores an incoming notice if we already have it" do
|
test "it ignores an incoming notice if we already have it" do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
|
|
||||||
|
@ -147,9 +117,7 @@ test "it does not crash if the object in inReplyTo can't be fetched" do
|
||||||
data
|
data
|
||||||
|> Map.put("object", object)
|
|> Map.put("object", object)
|
||||||
|
|
||||||
assert capture_log(fn ->
|
assert {:ok, _returned_activity} = Transmogrifier.handle_incoming(data)
|
||||||
{:ok, _returned_activity} = Transmogrifier.handle_incoming(data)
|
|
||||||
end) =~ "[warn] Couldn't fetch \"https://404.site/whatever\", error: nil"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it does not work for deactivated users" do
|
test "it does not work for deactivated users" do
|
||||||
|
@ -174,8 +142,8 @@ test "it works for incoming notices" do
|
||||||
assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
|
assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
|
|
||||||
assert data["cc"] == [
|
assert data["cc"] == [
|
||||||
"http://mastodon.example.org/users/admin/followers",
|
"http://localtesting.pleroma.lol/users/lain",
|
||||||
"http://localtesting.pleroma.lol/users/lain"
|
"http://mastodon.example.org/users/admin/followers"
|
||||||
]
|
]
|
||||||
|
|
||||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||||
|
@ -188,8 +156,8 @@ test "it works for incoming notices" do
|
||||||
assert object_data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
|
assert object_data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
|
|
||||||
assert object_data["cc"] == [
|
assert object_data["cc"] == [
|
||||||
"http://mastodon.example.org/users/admin/followers",
|
"http://localtesting.pleroma.lol/users/lain",
|
||||||
"http://localtesting.pleroma.lol/users/lain"
|
"http://mastodon.example.org/users/admin/followers"
|
||||||
]
|
]
|
||||||
|
|
||||||
assert object_data["actor"] == "http://mastodon.example.org/users/admin"
|
assert object_data["actor"] == "http://mastodon.example.org/users/admin"
|
||||||
|
@ -221,8 +189,25 @@ test "it works for incoming notices with hashtags" do
|
||||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
object = Object.normalize(data["object"], fetch: false)
|
object = Object.normalize(data["object"], fetch: false)
|
||||||
|
|
||||||
assert Enum.at(Object.tags(object), 2) == "moo"
|
assert match?(
|
||||||
assert Object.hashtags(object) == ["moo"]
|
%{
|
||||||
|
"href" => "http://localtesting.pleroma.lol/users/lain",
|
||||||
|
"name" => "@lain@localtesting.pleroma.lol",
|
||||||
|
"type" => "Mention"
|
||||||
|
},
|
||||||
|
Enum.at(object.data["tag"], 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert match?(
|
||||||
|
%{
|
||||||
|
"href" => "http://mastodon.example.org/tags/moo",
|
||||||
|
"name" => "#moo",
|
||||||
|
"type" => "Hashtag"
|
||||||
|
},
|
||||||
|
Enum.at(object.data["tag"], 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "moo" == Enum.at(object.data["tag"], 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it works for incoming notices with contentMap" do
|
test "it works for incoming notices with contentMap" do
|
||||||
|
@ -276,13 +261,11 @@ test "it ensures that address fields become lists" do
|
||||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||||
|> Jason.decode!()
|
|> Jason.decode!()
|
||||||
|> Map.put("actor", user.ap_id)
|
|> Map.put("actor", user.ap_id)
|
||||||
|> Map.put("to", nil)
|
|
||||||
|> Map.put("cc", nil)
|
|> Map.put("cc", nil)
|
||||||
|
|
||||||
object =
|
object =
|
||||||
data["object"]
|
data["object"]
|
||||||
|> Map.put("attributedTo", user.ap_id)
|
|> Map.put("attributedTo", user.ap_id)
|
||||||
|> Map.put("to", nil)
|
|
||||||
|> Map.put("cc", nil)
|
|> Map.put("cc", nil)
|
||||||
|> Map.put("id", user.ap_id <> "/activities/12345678")
|
|> Map.put("id", user.ap_id <> "/activities/12345678")
|
||||||
|
|
||||||
|
@ -290,8 +273,7 @@ test "it ensures that address fields become lists" do
|
||||||
|
|
||||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
assert !is_nil(data["to"])
|
refute is_nil(data["cc"])
|
||||||
assert !is_nil(data["cc"])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it strips internal likes" do
|
test "it strips internal likes" do
|
||||||
|
@ -310,9 +292,11 @@ test "it strips internal likes" do
|
||||||
object = Map.put(data["object"], "likes", likes)
|
object = Map.put(data["object"], "likes", likes)
|
||||||
data = Map.put(data, "object", object)
|
data = Map.put(data, "object", object)
|
||||||
|
|
||||||
{:ok, %Activity{object: object}} = Transmogrifier.handle_incoming(data)
|
{:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
refute Map.has_key?(object.data, "likes")
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
|
assert object.data["likes"] == []
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it strips internal reactions" do
|
test "it strips internal reactions" do
|
||||||
|
@ -330,70 +314,46 @@ test "it strips internal reactions" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it correctly processes messages with non-array to field" do
|
test "it correctly processes messages with non-array to field" do
|
||||||
user = insert(:user)
|
data =
|
||||||
|
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("to", "https://www.w3.org/ns/activitystreams#Public")
|
||||||
|
|> put_in(["object", "to"], "https://www.w3.org/ns/activitystreams#Public")
|
||||||
|
|
||||||
message = %{
|
assert {:ok, activity} = Transmogrifier.handle_incoming(data)
|
||||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
|
||||||
"to" => "https://www.w3.org/ns/activitystreams#Public",
|
|
||||||
"type" => "Create",
|
|
||||||
"object" => %{
|
|
||||||
"content" => "blah blah blah",
|
|
||||||
"type" => "Note",
|
|
||||||
"attributedTo" => user.ap_id,
|
|
||||||
"inReplyTo" => nil
|
|
||||||
},
|
|
||||||
"actor" => user.ap_id
|
|
||||||
}
|
|
||||||
|
|
||||||
assert {:ok, activity} = Transmogrifier.handle_incoming(message)
|
assert [
|
||||||
|
"http://localtesting.pleroma.lol/users/lain",
|
||||||
|
"http://mastodon.example.org/users/admin/followers"
|
||||||
|
] == activity.data["cc"]
|
||||||
|
|
||||||
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"]
|
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it correctly processes messages with non-array cc field" do
|
test "it correctly processes messages with non-array cc field" do
|
||||||
user = insert(:user)
|
data =
|
||||||
|
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("cc", "http://mastodon.example.org/users/admin/followers")
|
||||||
|
|> put_in(["object", "cc"], "http://mastodon.example.org/users/admin/followers")
|
||||||
|
|
||||||
message = %{
|
assert {:ok, activity} = Transmogrifier.handle_incoming(data)
|
||||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
|
||||||
"to" => user.follower_address,
|
|
||||||
"cc" => "https://www.w3.org/ns/activitystreams#Public",
|
|
||||||
"type" => "Create",
|
|
||||||
"object" => %{
|
|
||||||
"content" => "blah blah blah",
|
|
||||||
"type" => "Note",
|
|
||||||
"attributedTo" => user.ap_id,
|
|
||||||
"inReplyTo" => nil
|
|
||||||
},
|
|
||||||
"actor" => user.ap_id
|
|
||||||
}
|
|
||||||
|
|
||||||
assert {:ok, activity} = Transmogrifier.handle_incoming(message)
|
assert ["http://mastodon.example.org/users/admin/followers"] == activity.data["cc"]
|
||||||
|
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"]
|
||||||
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
|
|
||||||
assert [user.follower_address] == activity.data["to"]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it correctly processes messages with weirdness in address fields" do
|
test "it correctly processes messages with weirdness in address fields" do
|
||||||
user = insert(:user)
|
data =
|
||||||
|
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("cc", ["http://mastodon.example.org/users/admin/followers", ["¿"]])
|
||||||
|
|> put_in(["object", "cc"], ["http://mastodon.example.org/users/admin/followers", ["¿"]])
|
||||||
|
|
||||||
message = %{
|
assert {:ok, activity} = Transmogrifier.handle_incoming(data)
|
||||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
|
||||||
"to" => [nil, user.follower_address],
|
|
||||||
"cc" => ["https://www.w3.org/ns/activitystreams#Public", ["¿"]],
|
|
||||||
"type" => "Create",
|
|
||||||
"object" => %{
|
|
||||||
"content" => "…",
|
|
||||||
"type" => "Note",
|
|
||||||
"attributedTo" => user.ap_id,
|
|
||||||
"inReplyTo" => nil
|
|
||||||
},
|
|
||||||
"actor" => user.ap_id
|
|
||||||
}
|
|
||||||
|
|
||||||
assert {:ok, activity} = Transmogrifier.handle_incoming(message)
|
assert ["http://mastodon.example.org/users/admin/followers"] == activity.data["cc"]
|
||||||
|
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"]
|
||||||
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
|
|
||||||
assert [user.follower_address] == activity.data["to"]
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -419,7 +379,11 @@ test "schedules background fetching of `replies` items if max thread depth limit
|
||||||
} do
|
} do
|
||||||
clear_config([:instance, :federation_incoming_replies_max_depth], 10)
|
clear_config([:instance, :federation_incoming_replies_max_depth], 10)
|
||||||
|
|
||||||
{:ok, _activity} = Transmogrifier.handle_incoming(data)
|
{:ok, activity} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
object = Object.normalize(activity.data["object"])
|
||||||
|
|
||||||
|
assert object.data["replies"] == items
|
||||||
|
|
||||||
for id <- items do
|
for id <- items do
|
||||||
job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1}
|
job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1}
|
||||||
|
@ -442,45 +406,38 @@ test "does NOT schedule background fetching of `replies` beyond max thread depth
|
||||||
setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
|
setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
user = insert(:user)
|
replies = %{
|
||||||
|
"type" => "Collection",
|
||||||
|
"items" => [Utils.generate_object_id(), Utils.generate_object_id()]
|
||||||
|
}
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{status: "post1"})
|
activity =
|
||||||
|
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Kernel.put_in(["object", "replies"], replies)
|
||||||
|
|
||||||
{:ok, reply1} =
|
%{activity: activity}
|
||||||
CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: activity.id})
|
|
||||||
|
|
||||||
{:ok, reply2} =
|
|
||||||
CommonAPI.post(user, %{status: "reply2", in_reply_to_status_id: activity.id})
|
|
||||||
|
|
||||||
replies_uris = Enum.map([reply1, reply2], fn a -> a.object.data["id"] end)
|
|
||||||
|
|
||||||
{:ok, federation_output} = Transmogrifier.prepare_outgoing(activity.data)
|
|
||||||
|
|
||||||
Repo.delete(activity.object)
|
|
||||||
Repo.delete(activity)
|
|
||||||
|
|
||||||
%{federation_output: federation_output, replies_uris: replies_uris}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "schedules background fetching of `replies` items if max thread depth limit allows", %{
|
test "schedules background fetching of `replies` items if max thread depth limit allows", %{
|
||||||
federation_output: federation_output,
|
activity: activity
|
||||||
replies_uris: replies_uris
|
|
||||||
} do
|
} do
|
||||||
clear_config([:instance, :federation_incoming_replies_max_depth], 1)
|
clear_config([:instance, :federation_incoming_replies_max_depth], 1)
|
||||||
|
|
||||||
{:ok, _activity} = Transmogrifier.handle_incoming(federation_output)
|
assert {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(activity)
|
||||||
|
object = Object.normalize(data["object"])
|
||||||
|
|
||||||
for id <- replies_uris do
|
for id <- object.data["replies"] do
|
||||||
job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1}
|
job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1}
|
||||||
assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args)
|
assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows",
|
test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows",
|
||||||
%{federation_output: federation_output} do
|
%{activity: activity} do
|
||||||
clear_config([:instance, :federation_incoming_replies_max_depth], 0)
|
clear_config([:instance, :federation_incoming_replies_max_depth], 0)
|
||||||
|
|
||||||
{:ok, _activity} = Transmogrifier.handle_incoming(federation_output)
|
{:ok, _activity} = Transmogrifier.handle_incoming(activity)
|
||||||
|
|
||||||
assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == []
|
assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == []
|
||||||
end
|
end
|
||||||
|
@ -498,6 +455,7 @@ test "successfully reserializes a message with inReplyTo == nil" do
|
||||||
"object" => %{
|
"object" => %{
|
||||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
"cc" => [],
|
"cc" => [],
|
||||||
|
"id" => Utils.generate_object_id(),
|
||||||
"type" => "Note",
|
"type" => "Note",
|
||||||
"content" => "Hi",
|
"content" => "Hi",
|
||||||
"inReplyTo" => nil,
|
"inReplyTo" => nil,
|
||||||
|
@ -522,6 +480,7 @@ test "successfully reserializes a message with AS2 objects in IR" do
|
||||||
"object" => %{
|
"object" => %{
|
||||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
"cc" => [],
|
"cc" => [],
|
||||||
|
"id" => Utils.generate_object_id(),
|
||||||
"type" => "Note",
|
"type" => "Note",
|
||||||
"content" => "Hi",
|
"content" => "Hi",
|
||||||
"inReplyTo" => nil,
|
"inReplyTo" => nil,
|
||||||
|
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||||
alias Pleroma.Tests.ObanHelpers
|
alias Pleroma.Tests.ObanHelpers
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.AdminAPI.AccountView
|
alias Pleroma.Web.AdminAPI.AccountView
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
@ -159,8 +160,7 @@ test "it adds the json-ld context and the conversation property" do
|
||||||
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})
|
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})
|
||||||
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||||
|
|
||||||
assert modified["@context"] ==
|
assert modified["@context"] == Utils.make_json_ld_header()["@context"]
|
||||||
Pleroma.Web.ActivityPub.Utils.make_json_ld_header()["@context"]
|
|
||||||
|
|
||||||
assert modified["object"]["conversation"] == modified["context"]
|
assert modified["object"]["conversation"] == modified["context"]
|
||||||
end
|
end
|
||||||
|
@ -446,7 +446,7 @@ test "moves non-explicitly mentioned actors to cc", %{user: user} do
|
||||||
end)
|
end)
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed_object = Transmogrifier.fix_explicit_addressing(object)
|
fixed_object = Transmogrifier.fix_explicit_addressing(object, user.follower_address)
|
||||||
assert Enum.all?(explicitly_mentioned_actors, &(&1 in fixed_object["to"]))
|
assert Enum.all?(explicitly_mentioned_actors, &(&1 in fixed_object["to"]))
|
||||||
refute "https://social.beepboop.ga/users/dirb" in fixed_object["to"]
|
refute "https://social.beepboop.ga/users/dirb" in fixed_object["to"]
|
||||||
assert "https://social.beepboop.ga/users/dirb" in fixed_object["cc"]
|
assert "https://social.beepboop.ga/users/dirb" in fixed_object["cc"]
|
||||||
|
@ -459,7 +459,7 @@ test "does not move actor's follower collection to cc", %{user: user} do
|
||||||
"cc" => []
|
"cc" => []
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed_object = Transmogrifier.fix_explicit_addressing(object)
|
fixed_object = Transmogrifier.fix_explicit_addressing(object, user.follower_address)
|
||||||
assert user.follower_address in fixed_object["to"]
|
assert user.follower_address in fixed_object["to"]
|
||||||
refute user.follower_address in fixed_object["cc"]
|
refute user.follower_address in fixed_object["cc"]
|
||||||
end
|
end
|
||||||
|
@ -473,7 +473,7 @@ test "removes recipient's follower collection from cc", %{user: user} do
|
||||||
"cc" => [user.follower_address, recipient.follower_address]
|
"cc" => [user.follower_address, recipient.follower_address]
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed_object = Transmogrifier.fix_explicit_addressing(object)
|
fixed_object = Transmogrifier.fix_explicit_addressing(object, user.follower_address)
|
||||||
|
|
||||||
assert user.follower_address in fixed_object["cc"]
|
assert user.follower_address in fixed_object["cc"]
|
||||||
refute recipient.follower_address in fixed_object["cc"]
|
refute recipient.follower_address in fixed_object["cc"]
|
||||||
|
|
|
@ -539,8 +539,8 @@ test "it copies emoji from the subject of the parent post" do
|
||||||
spoiler_text: ":joker_smile:"
|
spoiler_text: ":joker_smile:"
|
||||||
})
|
})
|
||||||
|
|
||||||
assert Object.normalize(reply_activity).data["emoji"][":joker_smile:"]
|
assert Object.normalize(reply_activity).data["emoji"]["joker_smile"]
|
||||||
refute Object.normalize(reply_activity).data["emoji"][":joker_disapprove:"]
|
refute Object.normalize(reply_activity).data["emoji"]["joker_disapprove"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "deactivated users can't post" do
|
test "deactivated users can't post" do
|
||||||
|
|
|
@ -123,7 +123,8 @@ test "successfully processes incoming AP docs with correct origin" do
|
||||||
"type" => "Note",
|
"type" => "Note",
|
||||||
"content" => "hi world!",
|
"content" => "hi world!",
|
||||||
"id" => "http://mastodon.example.org/users/admin/objects/1",
|
"id" => "http://mastodon.example.org/users/admin/objects/1",
|
||||||
"attributedTo" => "http://mastodon.example.org/users/admin"
|
"attributedTo" => "http://mastodon.example.org/users/admin",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
},
|
},
|
||||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
}
|
}
|
||||||
|
@ -145,7 +146,8 @@ test "rejects incoming AP docs with incorrect origin" do
|
||||||
"type" => "Note",
|
"type" => "Note",
|
||||||
"content" => "hi world!",
|
"content" => "hi world!",
|
||||||
"id" => "http://mastodon.example.org/users/admin/objects/1",
|
"id" => "http://mastodon.example.org/users/admin/objects/1",
|
||||||
"attributedTo" => "http://mastodon.example.org/users/admin"
|
"attributedTo" => "http://mastodon.example.org/users/admin",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
},
|
},
|
||||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
@ -185,16 +186,16 @@ test "404 for private status", %{conn: conn, user: user} do
|
||||||
test "302 for remote cached status", %{conn: conn, user: user} do
|
test "302 for remote cached status", %{conn: conn, user: user} do
|
||||||
message = %{
|
message = %{
|
||||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"type" => "Create",
|
||||||
|
"actor" => user.ap_id,
|
||||||
|
"object" => %{
|
||||||
"to" => user.follower_address,
|
"to" => user.follower_address,
|
||||||
"cc" => "https://www.w3.org/ns/activitystreams#Public",
|
"cc" => "https://www.w3.org/ns/activitystreams#Public",
|
||||||
"type" => "Create",
|
"id" => Utils.generate_object_id(),
|
||||||
"object" => %{
|
|
||||||
"content" => "blah blah blah",
|
"content" => "blah blah blah",
|
||||||
"type" => "Note",
|
"type" => "Note",
|
||||||
"attributedTo" => user.ap_id,
|
"attributedTo" => user.ap_id
|
||||||
"inReplyTo" => nil
|
}
|
||||||
},
|
|
||||||
"actor" => user.ap_id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert {:ok, activity} = Transmogrifier.handle_incoming(message)
|
assert {:ok, activity} = Transmogrifier.handle_incoming(message)
|
||||||
|
|
Loading…
Reference in a new issue