akkoma/lib/pleroma/web/activity_pub/object_validator.ex

240 lines
7.9 KiB
Elixir
Raw Normal View History

# 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.ObjectValidator do
@moduledoc """
This module is responsible for validating an object (which can be an activity)
and checking if it is both well formed and also compatible with our view of
the system.
"""
@behaviour Pleroma.Web.ActivityPub.ObjectValidator.Validating
alias Pleroma.Activity
alias Pleroma.EctoType.ActivityPub.ObjectValidators
2020-06-17 01:31:06 -06:00
alias Pleroma.Object
2020-08-19 18:00:04 -06:00
alias Pleroma.Object.Containment
2019-10-23 04:18:05 -06:00
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.AddRemoveValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator
2020-06-11 12:43:01 -06:00
alias Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator
2020-10-21 02:23:10 -06:00
alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator
2020-08-19 18:00:04 -06:00
alias Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator
2020-04-08 07:55:43 -06:00
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator
2020-08-20 10:41:42 -06:00
alias Pleroma.Web.ActivityPub.ObjectValidators.EventValidator
2020-07-06 07:57:19 -06:00
alias Pleroma.Web.ActivityPub.ObjectValidators.FollowValidator
2020-04-20 04:29:19 -06:00
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
2020-06-11 12:23:10 -06:00
alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator
2020-05-07 07:09:37 -06:00
alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator
@impl true
def validate(object, meta)
def validate(%{"type" => "Block"} = block_activity, meta) do
with {:ok, block_activity} <-
block_activity
|> BlockValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
block_activity = stringify_keys(block_activity)
outgoing_blocks = Pleroma.Config.get([:activitypub, :outgoing_blocks])
meta =
if !outgoing_blocks do
Keyword.put(meta, :do_not_federate, true)
else
meta
end
{:ok, block_activity, meta}
end
end
2020-05-05 06:17:47 -06:00
def validate(%{"type" => "Undo"} = object, meta) do
with {:ok, object} <-
2020-05-07 06:52:37 -06:00
object
|> UndoValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
object = stringify_keys(object)
undone_object = Activity.get_by_ap_id(object["object"])
meta =
meta
|> Keyword.put(:object_data, undone_object.data)
2020-05-05 06:17:47 -06:00
{:ok, object, meta}
end
end
def validate(%{"type" => "Delete"} = object, meta) do
with cng <- DeleteValidator.cast_and_validate(object),
do_not_federate <- DeleteValidator.do_not_federate?(cng),
{:ok, object} <- Ecto.Changeset.apply_action(cng, :insert) do
object = stringify_keys(object)
meta = Keyword.put(meta, :do_not_federate, do_not_federate)
{:ok, object, meta}
end
end
2020-06-11 12:23:10 -06:00
def validate(
%{"type" => "Create", "object" => %{"type" => "ChatMessage"} = object} = create_activity,
meta
) do
2020-04-28 09:29:54 -06:00
with {:ok, object_data} <- cast_and_apply(object),
meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),
{:ok, create_activity} <-
create_activity
|> CreateChatMessageValidator.cast_and_validate(meta)
2020-04-16 07:21:47 -06:00
|> Ecto.Changeset.apply_action(:insert) do
2020-04-28 09:29:54 -06:00
create_activity = stringify_keys(create_activity)
{:ok, create_activity, meta}
2020-04-08 07:55:43 -06:00
end
end
2020-06-11 12:23:10 -06:00
def validate(
2020-06-11 12:43:01 -06:00
%{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
2020-06-11 12:23:10 -06:00
meta
2020-06-11 12:43:01 -06:00
)
2020-10-21 02:23:10 -06:00
when objtype in ~w[Question Answer Audio Video Event Article Note Page] do
2020-06-11 12:23:10 -06:00
with {:ok, object_data} <- cast_and_apply(object),
meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),
{:ok, create_activity} <-
create_activity
|> CreateGenericValidator.cast_and_validate(meta)
2020-06-11 12:23:10 -06:00
|> Ecto.Changeset.apply_action(:insert) do
create_activity = stringify_keys(create_activity)
{:ok, create_activity, meta}
end
end
2021-04-01 05:49:04 -06:00
def validate(%{"type" => type} = object, meta)
2020-10-21 02:23:10 -06:00
when type in ~w[Event Question Audio Video Article Note Page] do
2021-04-01 05:49:04 -06:00
validator =
case type do
"Event" -> EventValidator
"Question" -> QuestionValidator
"Audio" -> AudioVideoValidator
"Video" -> AudioVideoValidator
2020-10-21 02:23:10 -06:00
"Article" -> ArticleNotePageValidator
"Note" -> ArticleNotePageValidator
"Page" -> ArticleNotePageValidator
2021-04-01 05:49:04 -06:00
end
with {:ok, object} <-
object
|> validator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
object = stringify_keys(object)
# Insert copy of hashtags as strings for the non-hashtag table indexing
tag = (object["tag"] || []) ++ Object.hashtags(%Object{data: object})
object = Map.put(object, "tag", tag)
{:ok, object, meta}
end
end
def validate(%{"type" => type} = object, meta)
when type in ~w[Accept Reject Follow Update Like EmojiReact Announce
2021-04-01 05:49:04 -06:00
ChatMessage Answer] do
validator =
case type do
"Accept" -> AcceptRejectValidator
"Reject" -> AcceptRejectValidator
"Follow" -> FollowValidator
"Update" -> UpdateValidator
"Like" -> LikeValidator
"EmojiReact" -> EmojiReactValidator
"Announce" -> AnnounceValidator
"ChatMessage" -> ChatMessageValidator
"Answer" -> AnswerValidator
end
with {:ok, object} <-
object
|> validator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
2020-06-11 12:23:10 -06:00
object = stringify_keys(object)
{:ok, object, meta}
end
end
def validate(%{"type" => type} = object, meta) when type in ~w(Add Remove) do
with {:ok, object} <-
object
2021-03-19 08:25:12 -06:00
|> AddRemoveValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
object = stringify_keys(object)
{:ok, object, meta}
end
end
def validate(o, m), do: {:error, {:validator_not_set, {o, m}}}
2020-04-28 09:29:54 -06:00
def cast_and_apply(%{"type" => "ChatMessage"} = object) do
ChatMessageValidator.cast_and_apply(object)
end
2020-06-11 12:23:10 -06:00
def cast_and_apply(%{"type" => "Question"} = object) do
QuestionValidator.cast_and_apply(object)
end
2020-06-11 12:43:01 -06:00
def cast_and_apply(%{"type" => "Answer"} = object) do
AnswerValidator.cast_and_apply(object)
2020-06-11 12:43:01 -06:00
end
2020-08-19 18:00:04 -06:00
def cast_and_apply(%{"type" => type} = object) when type in ~w[Audio Video] do
AudioVideoValidator.cast_and_apply(object)
2020-08-17 17:48:42 -06:00
end
2020-08-20 10:41:42 -06:00
def cast_and_apply(%{"type" => "Event"} = object) do
EventValidator.cast_and_apply(object)
end
2020-10-21 02:23:10 -06:00
def cast_and_apply(%{"type" => type} = object) when type in ~w[Article Note Page] do
ArticleNotePageValidator.cast_and_apply(object)
2020-08-20 12:03:07 -06:00
end
2020-04-28 09:29:54 -06:00
def cast_and_apply(o), do: {:error, {:validator_not_set, o}}
# is_struct/1 appears in Elixir 1.11
2020-04-08 07:55:43 -06:00
def stringify_keys(%{__struct__: _} = object) do
object
|> Map.from_struct()
|> stringify_keys
end
2020-05-06 08:12:36 -06:00
def stringify_keys(object) when is_map(object) do
object
|> Enum.filter(fn {_, v} -> v != nil end)
2020-05-06 08:12:36 -06:00
|> Map.new(fn {key, val} -> {to_string(key), stringify_keys(val)} end)
end
2020-05-06 08:12:36 -06:00
def stringify_keys(object) when is_list(object) do
object
|> Enum.map(&stringify_keys/1)
end
def stringify_keys(object), do: object
def fetch_actor(object) do
2020-08-19 18:00:04 -06:00
with actor <- Containment.get_actor(object),
{:ok, actor} <- ObjectValidators.ObjectID.cast(actor) do
User.get_or_fetch_by_ap_id(actor)
end
end
def fetch_actor_and_object(object) do
fetch_actor(object)
Object.normalize(object["object"], fetch: true)
:ok
end
end