alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Object
alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
field(:type, :string)
field(:object, ObjectValidators.ObjectID)
field(:actor, ObjectValidators.ObjectID)
- field(:context, :string, autogenerate: {Utils, :generate_context_id, []})
+ field(:context, :string)
field(:to, ObjectValidators.Recipients, default: [])
field(:cc, ObjectValidators.Recipients, default: [])
field(:published, ObjectValidators.DateTime)
end
def cast_data(data) do
+ data =
+ data
+ |> fix()
+
%__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
+ defp fix(data) do
+ data =
+ data
+ |> CommonFixes.fix_actor()
+ |> CommonFixes.fix_activity_addressing()
+
+ with %Object{} = object <- Object.normalize(data["object"]) do
+ data
+ |> CommonFixes.fix_activity_context(object)
+ |> CommonFixes.fix_object_action_recipients(object)
+ else
+ _ -> data
+ end
end
defp validate_data(data_cng) do
|> validate_announcable()
end
- def validate_announcable(cng) do
+ defp 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),
end
end
- def validate_existing_announce(cng) do
+ defp validate_existing_announce(cng) do
actor = get_field(cng, :actor)
object = get_field(cng, :object)
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
alias Pleroma.EctoType.ActivityPub.ObjectValidators
+ alias Pleroma.Object
alias Pleroma.Object.Containment
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Transmogrifier
|> Transmogrifier.fix_implicit_addressing(follower_collection)
end
- def fix_activity_addressing(activity, _meta) do
+ def fix_activity_addressing(activity) do
%User{follower_address: follower_collection} = User.get_cached_by_ap_id(activity["actor"])
activity
|> Map.put("actor", actor)
|> Map.put("attributedTo", actor)
end
+
+ def fix_activity_context(data, %Object{data: %{"context" => object_context}}) do
+ data
+ |> Map.put("context", object_context)
+ end
+
+ def fix_object_action_recipients(%{"actor" => actor} = data, %Object{data: %{"actor" => actor}}) do
+ to = ((data["to"] || []) -- [actor]) |> Enum.uniq()
+
+ Map.put(data, "to", to)
+ end
+
+ def fix_object_action_recipients(data, %Object{data: %{"actor" => actor}}) do
+ to = ((data["to"] || []) ++ [actor]) |> Enum.uniq()
+
+ Map.put(data, "to", to)
+ end
end
alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Object
+ alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
import Ecto.Changeset
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
end
def cast_data(data) do
+ data =
+ data
+ |> fix()
+
%__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
- |> fix_context()
end
- def fix_context(cng) do
- object = get_field(cng, :object)
+ defp fix(data) do
+ data =
+ data
+ |> CommonFixes.fix_actor()
+ |> CommonFixes.fix_activity_addressing()
- with nil <- get_field(cng, :context),
- %Object{data: %{"context" => context}} <- Object.get_cached_by_ap_id(object) do
- cng
- |> put_change(:context, context)
+ with %Object{} = object <- Object.normalize(data["object"]) do
+ data
+ |> CommonFixes.fix_activity_context(object)
+ |> CommonFixes.fix_object_action_recipients(object)
else
- _ ->
- cng
+ _ -> data
end
end
- def validate_emoji(cng) do
+ defp validate_emoji(cng) do
content = get_field(cng, :content)
if Pleroma.Emoji.is_unicode_emoji?(content) do
alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Object
+ alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
alias Pleroma.Web.ActivityPub.Utils
import Ecto.Changeset
end
def cast_data(data) do
+ data =
+ data
+ |> fix()
+
%__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
- |> fix_recipients()
- |> fix_context()
- end
-
- def fix_context(cng) do
- object = get_field(cng, :object)
-
- with nil <- get_field(cng, :context),
- %Object{data: %{"context" => context}} <- Object.get_cached_by_ap_id(object) do
- cng
- |> put_change(:context, context)
- else
- _ ->
- cng
- end
end
- def fix_recipients(cng) do
- to = get_field(cng, :to)
- cc = get_field(cng, :cc)
- object = get_field(cng, :object)
+ defp fix(data) do
+ data =
+ data
+ |> CommonFixes.fix_actor()
+ |> CommonFixes.fix_activity_addressing()
- with {[], []} <- {to, cc},
- %Object{data: %{"actor" => actor}} <- Object.get_cached_by_ap_id(object),
- {:ok, actor} <- ObjectValidators.ObjectID.cast(actor) do
- cng
- |> put_change(:to, [actor])
+ with %Object{} = object <- Object.normalize(data["object"]) do
+ data
+ |> CommonFixes.fix_activity_context(object)
+ |> CommonFixes.fix_object_action_recipients(object)
else
- _ ->
- cng
+ _ -> data
end
end
|> validate_existing_like()
end
- def validate_existing_like(%{changes: %{actor: actor, object: object}} = cng) do
+ defp validate_existing_like(%{changes: %{actor: actor, object: object}} = cng) do
if Utils.get_existing_like(actor, %{data: %{"id" => object}}) do
cng
|> add_error(:actor, "already liked this object")
end
end
- def validate_existing_like(cng), do: cng
+ defp validate_existing_like(cng), do: cng
end
assert {:ok, _object, _meta} = ObjectValidator.validate(valid_announce, [])
end
+ test "keeps announced object context", %{valid_announce: valid_announce} do
+ assert %Object{data: %{"context" => object_context}} =
+ Object.get_cached_by_ap_id(valid_announce["object"])
+
+ {:ok, %{"context" => context}, _} =
+ valid_announce
+ |> Map.put("context", "https://example.org/invalid_context_id")
+ |> ObjectValidator.validate([])
+
+ assert context == object_context
+ end
+
test "returns an error if the object can't be found", %{valid_announce: valid_announce} do
without_object =
valid_announce
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,
assert LikeValidator.cast_and_validate(valid_like).valid?
end
- test "sets the 'to' field to the object actor if no recipients are given", %{
+ test "Add object actor from 'to' field if it doesn't owns the like", %{valid_like: valid_like} do
+ user = insert(:user)
+
+ object_actor = valid_like["actor"]
+
+ valid_like =
+ valid_like
+ |> Map.put("actor", user.ap_id)
+ |> Map.put("to", [])
+
+ {:ok, object, _meta} = ObjectValidator.validate(valid_like, [])
+ assert object_actor in object["to"]
+ end
+
+ test "Removes object actor from 'to' field if it owns the like", %{
valid_like: valid_like,
user: user
} do
- without_recipients =
+ valid_like =
valid_like
- |> Map.delete("to")
+ |> Map.put("to", [user.ap_id])
- {:ok, object, _meta} = ObjectValidator.validate(without_recipients, [])
-
- assert object["to"] == [user.ap_id]
+ {:ok, object, _meta} = ObjectValidator.validate(valid_like, [])
+ refute user.ap_id in object["to"]
end
test "sets the context field to the context of the object if no context is given", %{
assert object["context"] == post_activity.data["context"]
end
- test "it errors when the actor is missing or not known", %{valid_like: valid_like} do
- without_actor = Map.delete(valid_like, "actor")
-
- refute LikeValidator.cast_and_validate(without_actor).valid?
-
- with_invalid_actor = Map.put(valid_like, "actor", "invalidactor")
-
- refute LikeValidator.cast_and_validate(with_invalid_actor).valid?
- end
-
test "it errors when the object is missing or not known", %{valid_like: valid_like} do
without_object = Map.delete(valid_like, "object")
assert {:ok, %Activity{} = activity} = Relay.publish(note)
assert activity.data["type"] == "Announce"
assert activity.data["actor"] == service_actor.ap_id
- assert activity.data["to"] == [service_actor.follower_address]
+ assert service_actor.follower_address in activity.data["to"]
assert called(Pleroma.Web.Federator.publish(activity))
end
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")
- |> Jason.decode!()
- |> Map.put("object", Object.normalize(activity, fetch: false).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