@@ -8,6 +8,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do | |||
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 | |||
@@ -23,7 +24,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do | |||
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) | |||
@@ -36,6 +37,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do | |||
end | |||
def cast_data(data) do | |||
data = | |||
data | |||
|> fix() | |||
%__MODULE__{} | |||
|> changeset(data) | |||
end | |||
@@ -43,11 +48,21 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do | |||
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 | |||
@@ -60,7 +75,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator 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), | |||
@@ -91,7 +106,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do | |||
end | |||
end | |||
def validate_existing_announce(cng) do | |||
defp validate_existing_announce(cng) do | |||
actor = get_field(cng, :actor) | |||
object = get_field(cng, :object) | |||
@@ -4,6 +4,7 @@ | |||
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 | |||
@@ -36,7 +37,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do | |||
|> 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 | |||
@@ -57,4 +58,21 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do | |||
|> 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 |
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do | |||
alias Pleroma.EctoType.ActivityPub.ObjectValidators | |||
alias Pleroma.Object | |||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes | |||
import Ecto.Changeset | |||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations | |||
@@ -31,6 +32,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do | |||
end | |||
def cast_data(data) do | |||
data = | |||
data | |||
|> fix() | |||
%__MODULE__{} | |||
|> changeset(data) | |||
end | |||
@@ -38,28 +43,24 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do | |||
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 | |||
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do | |||
alias Pleroma.EctoType.ActivityPub.ObjectValidators | |||
alias Pleroma.Object | |||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes | |||
alias Pleroma.Web.ActivityPub.Utils | |||
import Ecto.Changeset | |||
@@ -31,6 +32,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do | |||
end | |||
def cast_data(data) do | |||
data = | |||
data | |||
|> fix() | |||
%__MODULE__{} | |||
|> changeset(data) | |||
end | |||
@@ -38,41 +43,20 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do | |||
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 | |||
@@ -85,7 +69,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do | |||
|> 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") | |||
@@ -95,5 +79,5 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do | |||
end | |||
end | |||
def validate_existing_like(cng), do: cng | |||
defp validate_existing_like(cng), do: cng | |||
end |
@@ -33,6 +33,18 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidationTest do | |||
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 | |||
@@ -51,16 +63,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidationTest do | |||
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, | |||
@@ -40,17 +40,30 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidationTest do | |||
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", %{ | |||
@@ -66,16 +79,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidationTest do | |||
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") | |||
@@ -148,7 +148,7 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do | |||
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 | |||
@@ -150,27 +150,4 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnnounceHandlingTest do | |||
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 |